【HOJ2662】Pieces Assignment(状压dp)

记录一个菜逼的成长。。

题目链接
PS:这是旧OJ的题,代码在新OJ里同样的题却T了,,或许加强数据了吧。然而我外加了个数组,却wa了。让我有点怀疑确定不是数据的问题?

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

经典的棋盘问题。
显然直接暴搜会T。
对于这类数据在10左右的而且搜索又会T的,普通dp又难以实现的,可以考虑状态压缩。。

dp[i][j][k] := 表示在第i行,已经放了j个棋子,此时第i行的状态为k。
可以得到状态转移方程
dp[i][j][x]+=dp[i1][jnum(x)][y] (num(x)表示状态为x的1的个数,也就是放了几个棋子)

我们要考虑如何去判状态的合法性。
先考虑一行的状态是否合法,就是不会有两个棋子相邻。
假设状态为x
可以用x&(x<<1)是否为0表示是否合法,为0则合法,否则不合法。
这个我们可以枚举一行的状态,预处理出合法的状态保存在数组里。

然后我们要考虑如何判相邻行的状态是否合法,这个比较容易。
假设上一行的状态为y,当前为x。
可以这样判断x&y是否为0,为0则合法,否则非法

ac代码。不过在新oj里会T。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ALL(v) (v).begin(),(v).end()
#define cl(a,b) memset(a,b,sizeof(a))
#define clr clear()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long LL;
const int maxn = 9;
LL dp[81][21][1<<maxn];
int mark[1<<maxn];
int num(int x)
{
    int ret = 0;
    while(x){
        ret += x & 1;
        x >>= 1;
    }
    return ret;
}
bool check(int x)
{
    if(x & (x<<1))return false;
    return true;
}
int main()
{
    int n,m,k;
    while(~scanf("%d%d%d",&n,&m,&k)){
        cl(dp,0);
        if(n > m)swap(n,m);
        int limit = (1<<n),len = 0;
        for( int i = 0; i < limit; i++ ){
            if(check(i)){
                dp[1][num(i)][len] = 1;
                mark[len++] = i;
            }
        }
        for( int i = 2; i <= m; i++ ){
            for( int j = 0; j <= k; j++ ){
                for( int x = 0; x < len; x++ ){
                    for( int y = 0; y < len; y++ ){
                        int tmp = num(mark[x]);
                        if(((mark[x] & mark[y]) == 0) && j >= tmp){
                            dp[i][j][x] += dp[i-1][j-tmp][y];
                        }
                    }
                }
            }
        }
        LL ans = 0;
        for( int i = 0; i < len; i++ ){
            ans += dp[m][k][i];
        }
        printf("%lld\n",ans);
    }
    return 0;
}

我加了个保存合法状态的1的个数的数组a[],在新oj的题里就wa了。不知道为什么。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ALL(v) (v).begin(),(v).end()
#define cl(a,b) memset(a,b,sizeof(a))
#define clr clear()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long LL;
const int maxn = 9;
LL dp[81][21][1<<maxn];
int mark[1<<maxn],a[1<<maxn];
int num(int x)
{
    int ret = 0;
    while(x){
        ret += x & 1;
        x >>= 1;
    }
    return ret;
}
bool check(int x)
{
    if(x & (x<<1))return false;
    return true;
}
int main()
{
    int n,m,k;
    while(~scanf("%d%d%d",&n,&m,&k)){
        cl(dp,0);cl(a,0);
        if(n > m)swap(n,m);
        int limit = (1<<n),len = 0;
        for( int i = 0; i < limit; i++ ){
            if(check(i)){
                dp[1][a[len] = num(i)][len] = 1;
                mark[len++] = i;
            }
        }
        for( int i = 2; i <= m; i++ ){
            for( int j = 0; j <= k; j++ ){
                for( int x = 0; x < len; x++ ){
                    for( int y = 0; y < len; y++ ){
                        int tmp = a[x];
                        if(((mark[x] & mark[y]) == 0) && j >= tmp){
                            dp[i][j][x] += dp[i-1][j-tmp][y];
                        }
                    }
                }
            }
        }
        LL ans = 0;
        for( int i = 0; i < len; i++ ){
            ans += dp[m][k][i];
        }
        printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值