zoukankan      html  css  js  c++  java
  • 「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
    ",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;
    }
    

      

  • 相关阅读:
    线程等待和通知
    什么是代码?code?
    代理(正向代理)和反向代理的区别是什么?
    什么是代理,什么是代理服务器,使用代理服务器的目的是什么?
    计算领域,编码的含义到底是什么?
    linux中,通过crontab -e编辑生成的定时任务,写在哪个文件中
    linux,shell中if else if的写法,if elif
    linux,shell脚本中获取脚本的名字,使用脚本的名字。
    linux,crontab定时任务中为脚本指定使用参数,crontab的脚本中是否可以带参数
    ssh在本地调用远程主机上的命令,不登录远程主机shell
  • 原文地址:https://www.cnblogs.com/cutemush/p/13546807.html
Copyright © 2011-2022 走看看