直接質問に騒ぎ、:
1594:スミアジャム
制限時間:1000ミリ秒のメモリ制限:524288キロバイト
で番号146:46番号を提出
説明[タイトル]
Tyvj周年のお祝いに来ている、サムはTyvjにとっては大きなケーキを作りたかったです。ケーキは、図の平面図です。 M矩形×Nの、それに分割されています のM側長×N ケーキされるように1×1の小さな正方形の領域( Nライン 行列Mの列)。まあ、すぐにケーキが、ケーキ良い見ていない確かに裸!したがって、サム・ジャムは、ケーキの表面に塗抹します。ジャム、三つ、即ち赤ジャム、マーマレード緑、青のジャム3つの数字であったジャムがあります 1,2,3。ケーキの視覚効果を確実にするためには、管理者は、死の命令を出した:隣接する領域は、ジャムの同じ種類を使用することを禁止されています。しかし、サムは、このコマンドを受信する前に、Kライン上の良いジャムケーキを描いており、変更することはできません。
今、サムが知りたい:どのように多くのジャム満足コーティングされた管理プログラムを作ります。出力にプログラムの数を喜ば 10 MOD 6 。スキームの条件を満たすことがある場合は、出力 0。
[Enter]を
3行の合計を入力します。
最初の行:N、M。
2列目:K;
第三行:Mの整数を表し、 Kラインプログラム。
詳細に説明した手紙のタイトルの意味は、参照、他の例を参照してください。
[出力]
出力のみの1行、実行可能なソリューションの合計数。
[サンプル入力]
2 2 1 2 3
[サンプル出力]
3
[注]
サンプル説明:
オプション1 | オプションII | オプション3 |
2 3 2 3 | 2 3 2 3 | 2 3 2 3 |
1 2 1 2 | 3 1 3 1 | 3 2 3 2 |
データ範囲とヒント:
、データの30%のための1。≤ N × M ≤ 20です。
データの60%については、1 ≤ N ≤ 1000 、1 ≤ M ≤ 3。 。
データの%、100 。1 ≤ N ≤ 10000 、1 ≤ M ≤ 。5。
[ソース]
この質問と最後の非常に類似した主題は、それが直接圧縮ダイナミックプログラミングの状態であるかを決定することができます。
それでは、どの圧縮の状態のでしょうか?この三色でジャムので、私たちは代わりに圧縮の方法を使用しての二元、三元状態を使用することはできません。
それぞれ、赤、緑、青のタイトルのジャム、そして我々は彼らが表現するために0,1,2を使用してみましょう。
だから、状態ラインは、あなたができるすべての良い列挙いくつかのバイナリのアプローチは、変更のコードを見ていることに注意することは限り、前処理をしてください。
最初の設計の状態は、我々は状態をf [i]は[J]と実行可能であるかどうか、[J]とi行目の状態を表現する方法です。このチャートの短い最初の分析では今何です。
前記k番目の行は、コーティングされた、と我々は、すなわち、部分は、図3(RGB)に示した3つの部分に分けることができます。
k番目の行が塗布されているので、それは、第0行とみなすことができます。明らかに、i行目の赤色および青色の部分は、同じ(それぞれ乱されていないため)の一部プログラムの数であるように。
しくじった絵画技法の小さなシリーズを無視します。
因此我们便利的行数只要是红色的行数就可以了,那么我们已经区分开了两个部分,怎样状态转移呢?
显然,f[i][j]=sum{f[i-1][k]},(前提是不发生冲突)其中a[j]是当前行的状态,a[k]是上一行的状态,那么这一行的方案数自然就是上一行方案数的和呗。
最后,我们要把所有状态的方案数都要加在一起,同时注意两个部分分别处理,方案总数是两个部分方案数的积。(乘法原理)
好了,详见注释,代码如下:
1 #include<iostream> 2 #define mod 1000000 3 using namespace std; 4 long long n,m,K,c[10000],cnt,a[10000],f[10001][200],s,num,correct[10000][10000],ans1,ans2; 5 inline int check(int x)//判断行内冲突 6 { 7 for(int i=m-1;i;i--) 8 if((x%c[i+1]/c[i])==(x%c[i]/c[i-1])) 9 return 0; 10 return 1; 11 } 12 inline int check2(int x,int y)//判断两行间的冲突 13 { 14 for(int i=m-1;i>=0;i--) 15 if((x%c[i+1]/c[i])==(y%c[i+1]/c[i])) 16 return 0; 17 return 1; 18 } 19 int main() 20 { 21 cin>>n>>m>>K;c[0]=1; 22 for(int i=1;i<=m;i++) 23 { 24 cin>>s; 25 num*=3; 26 num+=s-1;//三进制表示 27 c[i]=c[i-1]*3;//c是表示3进制数位的数组 28 } 29 if(!check(num)){cout<<0;return 0;}//如果第K行有冲突,那么直接输出0 30 for(int i=0;i<c[m];i++)//逐个枚举一行的状态 31 { 32 if(check(i)) a[++cnt]=i;//记录状态 33 if(i==num) f[0][cnt]=1;//初始化 34 } 35 for(int i=1;i<=cnt;i++)//枚举当前行状态 36 for(int j=1;j<=cnt;j++)//枚举上一行状态 37 if(check2(a[i],a[j])) correct[i][j]=1;//判断是否发生冲突 38 int x=max(K-1,n-K);int y=min(K-1,n-K);//分成两个部分 39 for(int i=1;i<=x;i++)//枚举行 40 for(int j=1;j<=cnt;j++)//枚举当前行状态 41 for(int k=1;k<=cnt;k++)//枚举上一行状态 42 if(correct[j][k]) f[i][j]=(f[i][j]+f[i-1][k])%mod;//注意是否发生冲突 43 for(int i=1;i<=cnt;i++)//枚举状态数 44 { 45 ans1+=f[x][i]; 46 ans1%=mod; 47 ans2+=f[y][i];//两个部分分别处理 48 ans2%=mod; 49 } 50 cout<<ans1*ans2%mod;//乘法原理 51 return 0; 52 }