[AHOI2009]中国象棋---------------递推

题目描述

这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!

输入输出格式

输入格式:

一行包含两个整数N,M,之间由一个空格隔开。

输出格式:

总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。

输入输出样例

输入样例#1:
1 3
输出样例#1:
7

说明

样例说明

除了3个格子里都塞满了炮以外,其它方案都是可行的,所以一共有2*2*2-1=7种方案。

数据范围

100%的数据中N和M均不超过100

50%的数据中N和M至少有一个数不超过8

30%的数据中N和M均不超过6

    因为每行每列只能够放置两枚棋子,所以考虑如何完全表示当前状态。

    • f【i】【j】【k】【q】表示到第 i 行为止,有 j 列上有零枚棋子,有 k 列上有一枚棋子,还有 q 列上有两枚棋子的状态的方案数。

    由于对于任意一种合法的状态必然满足 j+k+q=m 。所以我们可以省略掉一维。

    考虑转移,我们可以用大量的讨论来枚举每一行的棋子放在哪种列中,然后就可以愉快的 AC 了。

 1 #include<bits/stdc++.h>
 2 #define p 9999973
 3 #define ll long long
 4 using namespace std;
 5 int n,m;
 6 ll f[105][105][105],s,q,ans;
 7 int main()
 8 {
 9     scanf("%d%d",&n,&m);
10     f[0][0][0]=1;
11     for(int i=1;i<=n;++i)
12         for(int j=0;j<=m;++j)
13             for(int k=0;j+k<=m;++k)
14                 if(f[i-1][j][k])
15                 {
16                     s=f[i-1][j][k];
17                     q=m-j-k;
18                     (f[i][j][k]+=s)%=p;
19                     if(q-1>=0) (f[i][j+1][k]+=s*q)%=p;
20                     if(j-1>=0) (f[i][j-1][k+1]+=s*j)%=p;
21                     if(q-2>=0) (f[i][j+2][k]+=s*(q*(q-1)/2))%=p;
22                     if(j-2>=0) (f[i][j-2][k+2]+=s*(j*(j-1)/2))%=p;
23                     if(q-1>=0&&j-1>=0) (f[i][j][k+1]+=s*q*j)%=p;
24                 }
25     for(int i=0;i<=m;++i)
26         for(int j=0;i+j<=m;++j)
27             (ans+=f[n][i][j])%=p;
28     printf("%lld",ans);
29     return 0;
30 }
代码1

      这是这题的升级版,如果讨论的话难免出错,就算不出错的话,也很浪费时间。

      为了避免大量的讨论我们可以用多重循环或者dfs,那么就只需要确保转移的过

      程合法就不会有问题了。

 1 #include<bits/stdc++.h>
 2 #define p 19890604
 3 #define ll long long
 4 using namespace std;
 5 int n,m;
 6 ll f[2][66][66][66],vx,vy,vz,s,q,ans;
 7 void c(int a,int b,ll &pos)
 8 {
 9     pos=1;
10     for(int i=0;i<b;++i)
11         pos*=(a-i);
12     for(int i=1;i<=b;++i)
13         pos/=i;
14 }
15 int main()
16 {
17     scanf("%d%d",&n,&m);
18     f[0][m][0][0]=1;
19     for(int i=1;i<=n;++i)
20     {
21         for(int j=0;j<=m;++j)
22             for(int k=0;j+k<=m;++k)
23                 for(int q=0;j+k+q<=m;++q)
24                     f[i&1][j][k][q]=0;
25         for(int j=0;j<=m;++j)
26             for(int k=0;j+k<=m;++k)
27                 for(int q=0;j+k+q<=m;++q)
28                     if(f[i&1^1][j][k][q])
29                         for(int x=0;x<=3&&x<=j;++x)
30                         {
31                             c(j,x,vx);
32                             for(int y=0;x+y<=3&&y<=k&&k-y+x<=m;++y)
33                             {
34                                 c(k,y,vy);
35                                 for(int z=0;x+y+z<=3&&z<=q&&q-z+y<=m;++z)
36                                 {
37                                     c(q,z,vz);
38                                     (f[i&1][j-x][k-y+x][q-z+y]+=f[i&1^1][j][k][q]*vx%p*vy%p*vz%p)%=p;
39                                 }
40                             }
41                         }
42     }
43     for(int i=0;i<=m;++i)
44         for(int j=0;i+j<=m;++j)
45             for(int k=0;i+j+k<=m;++k)
46                 (ans+=f[n&1][i][j][k])%=p;
47     printf("%lld",ans);
48     return 0;
49 }
代码2
原文地址:https://www.cnblogs.com/wyher/p/9817366.html