标签:main alt 指正 space als cdc oid span psu
涂抹果酱
输入共三行。
第一行:N,M;
第二行:K;
第三行:M个整数,表示第K行的方案。
字母的详细含义见题目描述,其他参见样例。
输出仅一行,为可行的方案总数。
2 2 1 2 3
3
对于30%的数据,1≤N×M≤20;
对于60%的数据,1≤N≤1000,1≤M≤3;
对于100%的数据,1≤N≤10000,1≤M≤5。
题目思路:
因为有三种果酱,所以考虑三进制状压dp,首先初始化三进制的合法状态(相邻的不能一样),用一个vector保存
f[i][j]表示第i行状态为[v[j]]时前i行的方案数,judge用来判断上下两行是否合法,index代表第k行状态在vector中的下标。
因为第k行已经固定,所以要分为两个阶段,第一阶段为k行以上的方案数,第二阶段为k行以下的方案数,注意第一阶段结束后f[k][index]要重新归1。
最后的答案就是两个阶段相乘的方案数取模
代码如下:
#include<bits/stdc++.h> #define ll long long #define mod 1000000 using namespace std; ll n,m,k,a[6],f[10010][100],mode,ans1,ans2; vector<ll> v; void init(int x,int y)//初始化合法状态 { if(x==m) { v.push_back(y); return ; } else if(x==0) { init(1,0),init(1,1),init(1,2); return ; } if((int)(y/pow(3,x-1))%3==0) init(x+1,y+1*pow(3,x)),init(x+1,y+2*pow(3,x)); else if((int)(y/pow(3,x-1))%3==1) init(x+1,y+0*pow(3,x)),init(x+1,y+2*pow(3,x)); else init(x+1,y+0*pow(3,x)),init(x+1,y+1*pow(3,x)); } bool judge(int x,int y)//判断x状态和y状态分别为上下两行时是否合法 { int wei = m; while(wei--) { int a = x%3,b = y%3; if(a==b) return false; x/=3,y/=3; } return true; } int main() { cin>>n>>m>>k; for(int i = 0;i<m;i++) { cin>>a[i]; mode+=pow(3,m-1-i)*(a[i]-1);//mode记录第k行状态 } init(0,0); int index = find(v.begin(),v.end(),mode)-v.begin(); if(index==v.size())//如果第k行的状态不合法 { cout<<0; return 0; } if(k==1)//如果k为1,那么第一阶段方案数为1 { ans1 = 1; f[k][index] = 1; } if(k!=1) { for(int i = 0;i<v.size();i++) f[1][i] = 1; } for(int i = 2;i<=n;i++) { if(i==k) { for(int l = 0;l<v.size();l++) { if(judge(mode,v[l])) { f[i][index]+=f[i-1][l]; f[i][index]%=mod; } } ans1 = f[i][index]%mod; f[i][index] = 1; continue; } for(int j = 0;j<v.size();j++) { for(int l = 0;l<v.size();l++) { if(judge(v[j],v[l])&&f[i-1][l]) { f[i][j]+=f[i-1][l]; f[i][j]%=mod; } } } } for(int j = 0;j<v.size();j++) ans2 = (ans2+f[n][j])%mod; cout<<(ans1*ans2)%mod; return 0; }
如果有错误的地方,还请各位大佬指正。
标签:main alt 指正 space als cdc oid span psu
原文地址:https://www.cnblogs.com/loganacmer/p/11296808.html