zoukankan      html  css  js  c++  java
  • 【洛谷2051】[AHOI2009] 中国象棋(烦人的动态规划)

    点此看题面

    大致题意: 让你在一张(N*M)的棋盘上摆放炮,使其无法互相攻击,问有多少种摆法。

    辟谣

    听某大佬说这是一道状压(DP)题,于是兴冲冲地去做,看完数据范围彻底懵了:(N≤100)!这么大的数据范围压死你!

    好吧,其实这就是一道普通的(DP),与状压没有任何关系。

    其实状压可以用来骗分,能得50。

    考虑性质

    对于这种题目,第一步肯定是考虑有没有什么比较重要的性质。

    考虑炮的攻击方法,应该不难发现,每一行、每一列放的炮的数量不能超过(2)

    保证每行不超过(2),应该很简单。

    那么如何处理每列不超过(2)呢?这时就不难想到用(f_{i,j,k})来表示当前处理到第(i)行,有(j)列有(1)个炮,(k)列有(2)个炮时的方案数

    这样一来,应该就有一个比较清晰的思路了。

    接下来,就是无比烦人的分类讨论

    分类讨论

    不难发现,这题的状态有很多转移方式,因此就需要用到分类讨论。

    • 第一种情况: 这一行什么棋子也不放。

      直接将(f_{i,j,k})加上(f_{i-1,j,k})即可。

    • 第二种情况: 在没有放过炮的一列上放一个炮。

      比较显然应从状态((i-1,j-1,k))转移过来。

      (∵)在转移之前有((m-i-j+1))列是没有放过炮的,

      (∴)放炮的方式共有((m-i-j+1))种,

      (∴)(f_{i,j,k})加上((m-i-j+1)*f_{i-1,j-1,k})

    • 第三种情况: 在没有放过炮的两列上各放一个炮。

      此时的状态应从状态((i-1,j-2,k))转移过来。

      (∵)在转移之前有((m-i-j+2))列是没有放过炮的,

      (∴)放炮的方式共有(C_{m-j-k+2}^2)种,即(frac{(m-j-k+1)*(m-j-k+2)}2)种,

      (∴)(f_{i,j,k})加上(frac{(m-j-k+1)*(m-j-k+2)}2*f_{i-1,j-2,k})

    • 第四种情况: 在放过一个炮的一列上放一个炮。

      此时的状态应从状态((i-1,j+1,k-1))转移过来。

      (∵)在转移之前有((j+1))列是放过一个炮的,

      (∴)放炮的方式共有((j+1))种,

      (∴)(f_{i,j,k})加上((j+1)*f_{i-1,j+1,k-1})

    • 第五种情况: 在放过一个炮的两列上各放一个炮。

      此时的状态应从状态((i-1,j+2,k-2))转移过来。

      (∵)在转移之前有((j+2))列是放过一个炮的,

      (∴)放炮的方式共有(C_{j+2}^2)种,即(frac{(j+1)*(j+2)}2)种,

      (∴)(f_{i,j,k})加上(frac{(j+1)*(j+2)}2*f_{i-1,j+2,k-2})

    • 第六种情况: 在没有放过炮的一列和放过一个炮的一列上各放一个炮。

      此时的状态应从状态((i-1,j,k-1))转移过来。

      (∵)在转移之前有((m-j-k+1))列是没有放过炮的,有(j)列是放过一个炮的,

      (∴)放炮的方式共有((m-j-k+1)*j)种,

      (∴)(f_{i,j,k})加上((m-j-k+1)*j*f_{i-1,j,k-1})

    大致就是这(6)种情况了,不过取模之类的细节还是需要自己注意一下。

    代码

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define uint unsigned int
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define abs(x) ((x)<0?-(x):(x))
    #define INF 1e9
    #define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
    #define ten(x) (((x)<<3)+((x)<<1)) 
    #define MOD 9999973
    #define N 100
    using namespace std;
    int n,m; 
    class FIO
    {
        private:
            #define Fsize 100000
            #define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
            #define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
            int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
        public:
            FIO() {FinNow=FinEnd=Fin;}
            inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
            inline void read_char(char &x) {while(isspace(x=tc()));}
            inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
            inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
            inline void write_char(char x) {pc(x);}
            inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
            inline void end() {fwrite(Fout,1,FoutSize,stdout);}
    }F;
    class Class_DP//DP
    {
        private:
            int f[N+5][N+5][N+5];//用f[i][j][k]来表示当前处理到第i行,有j列有1个炮,k列有2个炮时的方案数
        public:
            inline int GetAns()
            {
                register int i,j,k,lim,ans=0;
                for(i=f[0][0][0]=1;i<=n;++i)//枚举行
                {
                    for(j=lim=min(i<<1,m);~j;--j) for(k=lim-j;~k;--k)//枚举放过一个棋子和两个棋子的列数
                    {
                        f[i][j][k]=f[i-1][j][k];//第一种情况
                        if(j>=1) Inc(f[i][j][k],1LL*f[i-1][j-1][k]*(m-j-k+1)%MOD);//第二种情况
                        if(j>=2) Inc(f[i][j][k],1LL*f[i-1][j-2][k]*((m-j-k+1)*(m-j-k+2)>>1)%MOD);//第三种情况
                        if(k>=1) Inc(f[i][j][k],1LL*f[i-1][j+1][k-1]*(j+1)%MOD);//第四种情况
                        if(k>=2) Inc(f[i][j][k],1LL*f[i-1][j+2][k-2]*((j+1)*(j+2)>>1)%MOD);//第五种情况
                        if(j>=1&&k>=1) Inc(f[i][j][k],1LL*f[i-1][j][k-1]*(m-j-k+1)%MOD*j%MOD);//第六种情况
                    }
                }
                for(i=lim=min(n<<1,m);~i;--i) for(j=lim-i;~j;--j) Inc(ans,f[n][i][j]);//统计答案
                return ans;
            } 
    }DP;
    int main()
    {
        F.read(n),F.read(m),F.write(DP.GetAns());
        return F.end(),0;
    }
    
  • 相关阅读:
    react redux 使用
    github 退出和别人共同开发的仓库
    在react package.json中配置代理解决跨域
    禁止浏览器sources下webpack文件 显示源码
    redux connect 装饰器配置和安装
    Odoo 在action的domain和context中使用self.env
    odoo 字段后面添加button按钮,页签tree再加group显示字段
    Odoo -- 开发者模式创建的群组、动作没有xml id怎么办
    Mac必备神器Homebrew mac下镜像飞速安装Homebrew教程
    Vue -- keyup、watch、computed、nrm的安装
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu2051.html
Copyright © 2011-2022 走看看