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

 

转载于:https://www.cnblogs.com/sweat123/p/5414446.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值