• [HOJ2662]Pieces Assignment<状态压缩dp>


    描述:

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

    输入:

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

    输出:

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

    样例输入:

    2 2 3 
    4 4 1

    样例输出:
    0

    16

    【思路】

    正常的想法是dp,一般的定义是dp[i][j]表示前i行放j个的方案数。。。

    当然这种定义并不能储存状态,我们可以用状态压缩dp来实现;

    这是一个棋盘,我们可以把放棋子理解为1,不放为0,那么一行就是由0,1组成,这些0,1在一起就是一个二进制数,即我们可以把每一行都转成十进制表示,而这个十进制的二进制就对应着这行的状态

    dp定为dp[i][j][k]表示在前i行已经放了j个棋子,当前这一行放的状态是k,一个数字k表示着一种合法的放置方式

    【步骤】

    一开始先预处理第一行可以怎么放

    因为不能两两相挨着,所以单独放一行的时候,这个二进制x&(x<<1)必须为0,假设x为10101,x<<1就是101010,&起来就是0,x就是合法的,记录到一个新的数组里

    然后就是枚举行,总的个数,上一行的状态x,当前行的状态y

    如果x状态和y状态&运算后==0并且j>=num(mark[x])//枚举的个数大于等于x状态下的棋子个数

    dp[i][j][x]+=dp[i-1][j-num(x)][y];

    最后再把第i行放k个的所有状态加起来就是ans了

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<queue>
     5 #include<cmath>
     6 #include<algorithm>
     7 #include<cstdlib>
     8 #define maxn 260
     9 #define LL long long
    10 using namespace std;
    11 
    12 LL dp[85][25][1<<6];//到第i行时填了j个,第i行是填第k种状态 
    13 int n,m,k;
    14 LL ans,tot,mark[maxn];
    15 
    16 int num(int x){
    17     int su=0;
    18     while(x){
    19         if(x&1)su++;
    20         x>>=1;
    21     }return su;
    22 }
    23 
    24 int main(){
    25     while(scanf("%d%d%d",&n,&m,&k)!=EOF){
    26         if(n<m)swap(n,m);//m是尽量小的那个
    27         tot=0;
    28         memset(dp,0,sizeof(dp));
    29         memset(mark,0,sizeof(mark));
    30         for(LL i=0;i<(1<<m);i++){
    31             if(!(i&(i<<1))){//当前行是合法的 
    32                 dp[1][num(i)][tot]=1;
    33                 mark[tot++]=i;//记录合法状态 
    34             }
    35         }        
    36         for(int i=2;i<=n;i++){
    37             for(int j=0;j<=k;j++){
    38                 for(LL x=0;x<tot;x++){
    39                     for(LL y=0;y<tot;y++){
    40                         if((mark[x]&mark[y])==0&&j>=num(mark[x])){
    41                             dp[i][j][x]+=dp[i-1][j-num(mark[x])][y];
    42                         }
    43                     }
    44                 }
    45             }
    46         }
    47         ans=0;
    48         for(LL i=0;i<tot;i++)
    49             ans+=dp[n][k][i];
    50         printf("%lld
    ",ans);
    51     }
    52 }
    View Code

    【总结】

    状态压缩dp要观察转移 ,定义要准确,灵活运用二进制

    1.x=(1<<(i-1))|x;这个运算可以让x二进制下的第i位变成1

    2.if(1<<(i<<1)&x==1)可以判断x二进制下的第i位是否为1,else就是不为1的情况

    3.x=x&(x-1)这个是去掉x二进制下最右边的那个1,对奇偶都成立,可以自行举例证明

  • 相关阅读:
    oracle 随机函数
    cmd和dos的区别
    SAP HANA 常用函数
    编程语言发展史
    PL/SQL Job
    ETL工具总结
    JDK、JRE、JVM三者间的关系
    Redis过滤器如何与Envoy代理一起使用
    apache配置https
    kubernetes监控和性能分析工具:heapster+influxdb+grafana
  • 原文地址:https://www.cnblogs.com/Danzel-Aria233/p/7721732.html
Copyright © 2020-2023  润新知