zoukankan      html  css  js  c++  java
  • hoj2662 状态压缩dp

    Pieces Assignment

    My Tags   (Edit)
      Source : zhouguyue
      Time limit : 1 sec   Memory limit : 64 M

    Submitted : 444, Accepted : 156

    Background

        有一个n*m的棋盘(n、m≤80,n*m≤80)要在棋盘上放k(k≤20)个棋子,使得任意两个棋子不相邻(每个棋子最多和周围4个棋子相邻)。求合法的方案总数。

    Input

        本题有多组测试数据,每组输入包含三个正整数n,m和k。

    Output

        对于每组输入,输出只有一个正整数,即合法的方案数。

    Sample Input

    2 2 3
    4 4 1
    

    Sample Output

    0
    16
     
    由于棋子位置不同,相同妻子个数的棋盘又有多种状态,所以考虑用状态压缩dp。
    dp[i][j][k]表示第i行,有j个棋子,状态为k的个数。
    dp[i][j][k] = sum{ dp[i-1][j-num[k][t] };(保证合法的状态下)。
     
     
     
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define INF 1000000007
    #define mod 100000000
    #define ll long long
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    using namespace std;
    const int MAXN = 21;
    int n,m,K,cnt;
    ll dp[81][21][300];
    ll q[1<<MAXN];
    int num[300];
    int getnum(int x)
    {
        int cnt = 0;
        while(x){
            if(x & 1)cnt ++;
            x >>= 1;
        }
        return cnt;
    }
    void Init()
    {
        cnt = 0;
        memset(num,0,sizeof(num));
        //得到所有满足条件的合法的状态
        for(int i = 0; i < (1 << m); i++){
            if(!(i & (i << 1))){
                num[cnt] = getnum(i);
                q[cnt++] = i;
    
            }
        }
    }
    int main()
    {
        while(~scanf("%d%d%d",&n,&m,&K)){
            if(n < m){
                swap(n,m);
            }
            Init();
            memset(dp,0,sizeof(dp));
            for(int i = 0; i < cnt; i++){
                if(num[i] > K)continue;
                dp[1][num[i]][i] = 1;//第一行能够放num[i]个棋的状态为i的个数有1个
            }
            for(int i = 2; i <= n; i++){//枚举行数
                for(int j = 0; j <= K; j++){//枚举棋子个数
                    for(int k = 0; k < cnt; k++){//枚举状态
                        if(num[k] > j)continue;//如果当前状态的num[k]个数大于j个 不合法
                        for(int t = 0; t < cnt; t++){
                            if((q[t]&q[k]) || num[t] > j)continue;//有相交或者个数不合法
                            dp[i][j][k] += dp[i-1][j-num[k]][t];//第i行个数为j个棋子的状态为k的个数 等于所有
                                                                //上一行用了j-num[k]个棋子的状态为t的和。
                        }
                    }
                }
            }
            ll ans = 0;
            for(int i = 0; i < cnt; i++){
                ans += dp[n][K][i];
            }
            cout<<ans<<endl;
        }
        return 0;
    }
  • 相关阅读:
    python活力练习Day13
    检测一个字符串在另外一个字符串中的位置
    Python活力练习Day12
    Python多进程与单进程效率对比
    HTML-Note
    Python判断自定义的参数格式是否正确
    图片的灰与彩
    Git常用命令
    Linux 单引号和双引号的区别
    类函数中获取进程池对象的地址
  • 原文地址:https://www.cnblogs.com/sweat123/p/5414446.html
Copyright © 2011-2022 走看看