zoukankan      html  css  js  c++  java
  • SGU 223 Little Kings

    状态压缩DP

    题意:输入n,m,一个n*n的棋盘,放入m个国王,国王不能相互攻击,有多少种放置的可能。国际象棋的国王的攻击范围就是它周围的一圈,一共8个格子(上下左右四个对角)

    poj 1185 炮兵布阵 非常相似 , 同样使用位运算加速,同样可以使用滚动数组

    代码有详细注释

    /*
    state[i]表示第i种状态,即一行内的国王不互相攻击的状态,king[i]对应这种状态有多少个国王
    
    关于相邻两行的状态是否能共存的问题,也就是两行的国王会不会相互攻击,state[k]表示第i行的状态,state[kk]表示第i-1行的状态
    state[k] & state[kk] = 1  , 说明在某一列上有国王直接对应着,攻击了
    state[k] & (state[kk]<<1) = 1 , 先将i-1行的状态左移一位,比较为1,说明相邻的两列有国王,那也是会攻击的
    初写的时候漏了这个,后来很快发现了 (state[k]<<1) & state[kk] = 1 ,这同样说明相邻两列有国王互相攻击
    
    按行DP,当前这行的信息只由上面那行决定,所以DP的状态定义也只和上层有关
    dp[i][j][k],表示到第i层为止,一共放了j个国王,且第i层的状态为state[k]
    所以我们要求的目标状态就是  sum{ dp[n][m][k] } 即全部n层一共放入了m个国王,其中第n层可能的状态为state[k]
    状态转移方程
    dp[i][j][k]=sum{ dp[i-1][mm][kk]} 即前i-1层放了mm个国王,第i-1层的状态为state[kk],
    第i层状态为state[i],对应的国王数加上mm刚好是j。
    */
    #include <cstdio>
    #include <cstring>
    #define N 15
    #define M 110
    #define MAX 550
    
    int state[MAX];  //记录一行以内可能的状态
    int king[MAX];   //对应每个状态放了多少个国王
    int nums;        //状态总数
    long long dp[N][M][MAX];   //dp[i][j][k],到第i行一共放了j个国王,第i行的状态为state[k]的方案数
    int n,m;
    
    void DP()
    {
        int i,j,k,c,mm,kk,cc;
        long long ans;
        memset(dp,0,sizeof(dp));
            
        //先初始化第1行的情况
        for(i=0; i<nums; i++) //枚举一行可能出现的状态
        {
            j=king[i];  //在这个状态下放了多少个国王
            if(j<=m) dp[1][j][i]++; //方案数计数,该行放的个数不能超过总数
        }
    
        for(i=2; i<n; i++) //从第2行dp到第n-1行
        {
            for(j=0; j<=m; j++)  //到第i行,一共放了j个国王
            {
                for(k=0; k<nums; k++) //当前第i行的状态
                {
                    c=king[k]; //第i行放了多少个国王
                    if(c>j) continue; //若当前行的国王数就超过总数j,那么跳过
                    mm=j-c;  //那么前i-1行的总数是mm
                    for(kk=0; kk<nums; kk++) //枚举第i-1行的状态
                    {
                        cc=king[kk];  //第i-1行放了多少个国王
                        if(cc>mm) continue; //若当前行的国王数就超过总数mm,那么跳过
                        if(state[k] & state[kk]) continue;
                        //第i行和第i-1行的国王有攻击
                        if(state[k] & (state[kk]<<1)) continue;
                        //第i行和第i-1行的国王有攻击
                        if(state[kk] & state[k]<<1)   continue;
                        //第i行和第i-1行的国王有攻击
                        dp[i][j][k] += dp[i-1][mm][kk];
                    }
                }
            }
        }
        
        ans=0;
        for(k=0; k<nums; k++) //枚举最后一行的状态
        {
            c=king[k];  //该行的国王数
            if(c>m) continue; //该行的国王数就超过总数,跳过
            j=m-c;  //前n-1行的总数
            for(kk=0; kk<nums; kk++)  //第n-1行的状态
            {
                cc=king[kk];  //第n-1行的状态
                if(cc>j) continue; //第n-1行的国王数超过了总数那么跳过
                if(state[k] & state[kk]) continue;
                //第i行和第i-1行的国王有攻击
                if(state[k] & (state[kk]<<1)) continue;
                //第i行和第i-1行的国王有攻击
                if(state[kk] & state[k]<<1)   continue;
                
                dp[n][m][k] += dp[n-1][j][kk];
            }
            ans += dp[n][m][k];
        }
    
        printf("%lld\n",ans);
    }
    
    void init_state()
    {
        nums=0;
        for(int i=0; i<(1<<n); i++) //所有可能的状态
        {
            if( i & (i<<1) ) continue; //行内互相攻击丢弃
            int t=i;
            king[nums]=0;
            while(t)  //统计在i状态下放了多少个国王
            { king[nums] += (t&1); t=t>>1; }
            state[nums++]=i;  //保存这个合法的状态
        }
    }
    
    int main()
    {
        //freopen("input.txt","r",stdin);
        //freopen("output.txt","w",stdout);
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            init_state();  //找出所有可能的状态
            DP();  //动态规划解决
        }
        return 0;
    }

    最近换了高亮和背景的配色,看着舒服多了

  • 相关阅读:
    APP Https双向认证抓包
    剖析XSS
    php连接mysql
    linux去掉某一字符开头的行
    memcached+php客户端
    memcached-repcached
    memcached+memadmin
    Linux GPT分区
    Linux查看文件夹大小
    linux挂载windwos共享文件
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2949031.html
Copyright © 2011-2022 走看看