HITOJ 2662 Pieces Assignment(状压DP)

Pieces Assignment

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

Submitted : 539, Accepted : 190

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

题目链接:HITOJ 2662

由于实验室内大神讲了高大上的状压DP,于是就就去看了一下,感觉还是比较好理解的,就是感觉状态转移对于我这种还没入门的就比较难想到了……

dp[i][j][k]表示当前已遍历过i行,总共用了j个棋子,且最后一行摆放状态为k的方案数,然后就是如何进行状态的转移呢?首先DP总得要初始化的吧,先考虑只有一行的情况(实际上初始化的时候可以把所有合法的行状态都保存下来方便之后遍历),一行里的方案数就只需要初始化dp[1][j][k1]

把一行看成m个位置,每一个位置都是0或1,1表示放了棋子,0表示没放,比如一行3个棋子,可以是这样101、110、010、000、111(二进制)等等,如何判断当前摆放情况是否合法呢,这里要用到位运算&,即当前的状态转换成10进制进行&运算,sta&(sta<<1),可以发现若存在两个或以上1相邻的情况,按位与的结果必定不为0即不合法,然后在不知道如何摆放的情况下遍历所有可能的方案,上面的例子三个位置一共有2*2*2=8种情况,分别是011、101、110、100、010、001、111、000,但其实把n位全部填满最多只会达到2n-1因此可以for (i=0; i<=(1<<3)-1; ++i),然后把每一个行状态判断一下并保存在合法状态的集合里,然后对每一行都进行判断,

转移方程就是dp[i][j][k1]+=dp[i-1][j-这一行二进制状态所含1的个数count][k2](j>=count&&k1与k2不冲突)

代码:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <bitset>
#include <string>
#include <deque>
#include <stack>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define INF 0x3f3f3f3f
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const double PI=acos(-1.0);
const int M=1<<9;
LL dp[82][22][M];//第i行、用了j个棋子、状态为第k个
int status[M];

inline bool check(const int &a,const int &b)
{
    return (a&b)==0;//这里要加括号,==的优先级比&高
}

int main(void)
{
    int n,m,k,i,j,pre,cur;
    while (~scanf("%d%d%d",&n,&m,&k))
    {
        CLR(dp,0);
        if(m>n)//m保持较小
            swap(n,m);

        int legalcnt=0;
        int totalsta=(1<<m)-1;
        for (i=0; i<=totalsta; ++i)
        {
            if(check(i,i>>1))
            {
                dp[1][bitset<10>(i).count()][legalcnt]=1LL;
                status[legalcnt++]=i;
            }
        }
        for (i=2; i<=n; ++i)
        {
            for (j=0; j<=k; ++j)
            {
                for (cur=0; cur<legalcnt; ++cur)
                {
                    for(pre=0; pre<legalcnt; ++pre)
                    {
                        int curuse=bitset<10>(status[cur]).count();
                        if(check(status[pre],status[cur])&&j>=curuse)
                        {
                            dp[i][j][cur]+=dp[i-1][j-curuse][pre];
                        }
                    }
                }
            }
        }
        LL r=0;
        for (i=0; i<legalcnt; ++i)
            r+=dp[n][k][i];
        printf("%lld\n",r);
    }
    return 0;
}
时间: 2024-10-27 05:01:04

HITOJ 2662 Pieces Assignment(状压DP)的相关文章

hoj 2662 Pieces Assignment 状态压缩dp入门

//hoj 2662 Pieces Assignment //有一个n*m的棋盘(n.m≤80,n*m≤80)要在棋盘上放k(k≤20)个棋子,使得任意两 //个棋子不相邻(每个棋子最多和周围4个棋子相邻).求合法的方案总数. // //算是另一个状态压缩dp入门吧 //dp[i][S][j]表示第i行的棋子状态是S(整数的二进制形式,比如5为 // ...101,省略号表示前导0,那一位上是1就表示哪一列上放了棋子,5表示第一列 //第三列放了棋子.)时已经放了j颗棋子,则转移方程为: //d

HDU 4628 Pieces(状压DP)题解

题意:n个字母,每次可以删掉一组非连续回文,问你最少删几次 思路:把所有回文找出来,然后状压DP 代码: #include<set> #include<map> #include<cmath> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include <iostream> #include<algorithm&

ZOJ3305Get Sauce 状压DP,

状压DP的题目留个纪念,首先题意一开始读错了,搞了好久,然后弄好了,觉得DFS可以,最后超时,修改了很久还是超时,没办法看了一下n的范围,然后觉得状压可以,但是没有直接推出来,就记忆化搜索了一下,可是一直错,莫名奇妙,然后没办法看了一下题解,发现了下面这个比较好的方法,然后按照这个方程去推,然后敲,也是WA了好多把,写的太搓了,没人家的清楚明了,唉~也算是给自己留个纪念,状压一直做的都不太好~唉~还好理解了, 参考了  http://blog.csdn.net/nash142857/articl

poj 2411 Mondriaan&#39;s Dream(状压DP)

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12232   Accepted: 7142 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

(状压dp)uva 10817 Headmaster&#39;s Headache

题目地址 1 #include <bits/stdc++.h> 2 typedef long long ll; 3 using namespace std; 4 const int MAX=1e5+5; 5 const int INF=1e9; 6 int s,m,n; 7 int cost[125]; 8 //char sta[MAX]; 9 string sta; 10 int able[125]; 11 int dp[125][1<<8][1<<8]; 12 in

HDU5816 Hearthstone(状压DP)

题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5816 Description Hearthstone is an online collectible card game from Blizzard Entertainment. Strategies and luck are the most important factors in this game. When you suffer a desperate situation an

HDU 4336 容斥原理 || 状压DP

状压DP :F(S)=Sum*F(S)+p(x1)*F(S^(1<<x1))+p(x2)*F(S^(1<<x2))...+1; F(S)表示取状态为S的牌的期望次数,Sum表示什么都不取得概率,p(x1)表示的是取x1的概率,最后要加一因为有又多拿了一次.整理一下就可以了. 1 #include <cstdio> 2 const int Maxn=23; 3 double F[1<<Maxn],p[Maxn]; 4 int n; 5 int main() 6

Travel(HDU 4284状压dp)

题意:给n个城市m条路的网图,pp在城市1有一定的钱,想游览这n个城市(包括1),到达一个城市要一定的花费,可以在城市工作赚钱,但前提有工作证(得到有一定的花费),没工作证不能在该城市工作,但可以走,一个城市只能工作一次,问pp是否能游览n个城市回到城市1. 分析:这个题想到杀怪(Survival(ZOJ 2297状压dp) 那个题,也是钱如果小于0就挂了,最后求剩余的最大钱数,先求出最短路和 Hie with the Pie(POJ 3311状压dp) 送披萨那个题相似. #include <

BZOJ 1087: [SCOI2005]互不侵犯King( 状压dp )

简单的状压dp... dp( x , h , s ) 表示当前第 x 行 , 用了 h 个 king , 当前行的状态为 s . 考虑转移 : dp( x , h , s ) = ∑ dp( x - 1 , h - cnt_1( s ) , s' ) ( s and s' 两行不冲突 , cnt_1( s ) 表示 s 状态用了多少个 king ) 我有各种预处理所以 code 的方程和这有点不一样 ------------------------------------------------