1. 程式人生 > 實用技巧 >「BZOJ1801」[Ahoi2009] chess 中國象棋

「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;
}