「BZOJ1801」[Ahoi2009] chess 中国象棋
在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!
输入输出格式
输入格式:
一行包含两个整数N,M,之间由一个空格隔开。
N和M均不超过6
输出格式:
总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。
输入输出样例
输入样例#1:
1 3
输出样例#1:
7
说明
样例说明
除了3个格子里都塞满了炮以外,其它方案都是可行的,所以一共有2*2*2-1=7种方案。
数据范围
100%的数据中N和M均不超过100
暴力做法:
#include<iostream> using namespace std; int ans=0; int n,m; bool chsp[10][10]={}; int col[100]={},row[100]={}; void print() { int i,j; for(i=1;i<=n;i++){ for(j=1;j<=m;j++) if (chsp[i][j]) cout<<"#"; else cout<<"-"; cout<<endl; } cout<<endl; } void tryy(int row) { int i,j; if(row>=n+1) { // print(); ans++; return; } tryy(row+1);//第row行不放炮 for(i=1;i<=m;i++) //第row行放炮,于是枚举下放在哪一列 //因为一行最多放两个炮,于是先枚举第一个炮放在哪一列 if (col[i]<2) { // col[i] 记录i列几个炮 col[i]++; chsp[row][i]=1; tryy(row+1); for(j=i+1;j<=m;j++) //枚举第二个炮放在哪一列 if (col[j]<2) {// col[i] 记录j列几个炮 col[j]++; chsp[row][j]=1; tryy(row+1); col[j]--; chsp[row][j]=0; } col[i]--; chsp[row][i]=0; } } int main() { cin>>n>>m; tryy(1); cout<<ans<<endl; return 0; }
动态规划,需要知道任意一列或一行最多放两个炮,炮数相同的列或行是没有区别的,剩下的看代码吧。
//每行每列最多放两个,可以讨论第i-1行到第i行的每一种情况 #include<complex> #include<cstdio> using namespace std; const int mod=9999973; const int N=101; int n,m; long long f[N][N][N]; //f[i][j][k]表示前i行有j列放了一个,k列放了两个按方案数 int qread() { int x=0; char ch=getchar(); while(ch<'0' || ch>'9')ch=getchar(); while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} return x; } void Add(long long &x,long long y) { x+=y; if(x>=mod)x%=mod; } int main() { scanf("%d%d",&n,&m); f[0][0][0]=1; for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) for(int k=0;k+j<=m;k++) { f[i][j][k]=f[i-1][j][k]; //不放 if(j) Add(f[i][j][k],f[i-1][j-1][k]*(m-k-j+1)); //放一个在没有棋子的列 if(k) Add(f[i][j][k],f[i-1][j+1][k-1]*(j+1)); //放一个在有一个棋子的列 if(k>=2) Add(f[i][j][k],f[i-1][j+2][k-2]*(j+2)*(j+1)>>1); //放两个在有一个棋子的列 if(j>=2) Add(f[i][j][k],f[i-1][j-2][k]*(m-k-j+2)*(m-k-j+1)>>1); //放两个在没有棋子的列 if(j && k) Add(f[i][j][k],f[i-1][j][k-1]*(m-k-j+1)*j); //放一个在没有棋子的列,另一个在有一个棋子的列 } long long ans=0; for(int i=0;i<=m;i++) for(int j=0;j+i<=m;j++) Add(ans,f[n][i][j]); printf("%d\n",ans); return 0; }
#include<cstring> #define MAX 101 #define MOD 9999973 using namespace std; long long dp[MAX][MAX][3] = {0}; int n, m; long long (int x, int y, int t) { if (x == 0 || y == 0)return 0; if (x == 1) { if (t == 0)return 1; if (t == 1)return y; if (t == 2)return y * (y - 1) / 2; } if (y == 1) { if (t == 0)return (DP(x - 1, 1, 0) + DP(x - 1, 1, 1) + DP(x - 1, 1, 2)) % MOD; if (t == 1)return x; if (t == 2)return 0; } if (t == 2) { if (x == 2)return y * (y - 1) / 2 * (y * y + y + 2) / 2; if (y == 2)return x * x; } if (dp[x][y][t] >= 0)return dp[x][y][t];//记忆化 //下面是状态转移,t=2时方程太长,分开计算 if (t == 0)return dp[x][y][t] = (DP(x - 1, y, 0) + DP(x - 1, y, 1) + DP(x - 1, y, 2)) % MOD; if (t == 1)return dp[x][y][t] = ((DP(x - 1, y - 1, 0) + DP(x - 1, y - 1, 1) + DP(x - 1, y - 1, 2)) % MOD + DP(y, x - 1, 1)) * y % MOD; int ans = 0; ans += (DP(x - 1, y - 2, 0) + DP(x - 1, y - 2, 1) + DP(x - 1, y - 2, 2)) * y * (y - 1) / 2 % MOD; ans += DP(y - 1, x - 1, 1) * y * (y - 1) % MOD; ans %= MOD; ans += DP(y - 1, x - 1, 2) * y * (y - 1) % MOD + (DP(x - 2, y - 2, 0) + DP(x - 2, y - 2, 1) + DP(x - 2, y - 2, 2)) % MOD * y * (y - 1) / 2 * (x - 1) % MOD; return dp[x][y][t] = ans % MOD; } int main() { cin >> n >> m; memset(dp, -1, sizeof(dp)); cout << (DP(n, m, 0) + DP(n, m, 1) + DP(n, m, 2)) % MOD << endl; return 0; }