// 此博文为迁移而来,写于2015年7月15日,不代表本人现在的观点与看法。原始地址:http://blog.sina.com.cn/s/blog_6022c4720102w6jf.html

1、前言
       动态规划,永远的痛。
       好了不扯远了。状态压缩动态规划,其实看名字还是较好理解的。我们在动态规划的时候,最重要的就在于状态的设计和状态转移方程。那么,如果当我们状态过多导致时间或空间不够的饿时候,就可以用到状态压缩。王队(@wyh2000)说状态压缩DP难起来的话会很难,但是今天我们只讲最最最基础的状态压缩方式,以后再慢慢补充。
 
2、区别
       ①有一个1*n的棋盘(n<=80),需要在上面放置k个棋子,使棋子之间不相邻,求方案数。
           那么这是一道很简单的棋盘型DP。设f[i][j][0]为前i个放置j个棋子的方案数且第i位必放,f[i][j][1]为前i个放置j个棋子的方案数且第i位必不放。则存在方程:
                   

       ②有一个m*n的棋盘(n*m<=80),需要在上面放置k个棋子,使棋子之间不相邻,求方案数。
           多了一个行的状态,这就让人费解了——我们并不能设置一个四维方程来表示状态。原来我们每一行只有一个格子,现在多个格子怎么表示呢?这里就要用到状态压缩了。这里提到的状态压缩的方式只是其中一种,相对比较简单的一种——二进制转换。我们注意到题目有一个特别鬼畜的数据范围——n*m<=80。这意味着什么?9*9=81>80,则min(n,m)的最大值为8。我们将m,n中较小的一个看做行(易得行列转换依旧等价),一行至多8个格子,我们令当前状态下格子若放置了棋子则记为1,未放置记为0,那么我们可以将一行的状态表示为一个二进制数,继而在状态转移的时候再转换为十进制。前文提到了最多8个数,所以数组中数值最大为2^8=256。设f[i][j][k]表示当前第i列,使用了j个棋子,当前行的状态为k(一个由二进制数转换过来的十进制数),则状态转移方程为:
        
            其中k`表示上一行的状态,num(k)表示在当前行状态为k的情况下棋子的总个数。
            当然我们要注意——如何判断当前这一行与上一行是否存在相邻的棋子?利用按位异或即可。见代码。
----------------------------------------------------------------------------------------------------
#include cstdio
#include cstring
#include algorithm
using namespace std;
 
long long f[81][1<<9][21];
 
inline int getNum(int x)
{
        int s=0,tmp=0;
        while (x)
        {
                if (s && (x & 1)) return -1;
                if (s=(x & 1)) tmp++;
                x=x>>1;
        }
        return tmp;
}
 
int main()
{
        int n,m,t;
        while (scanf("%d %d %d",&n,&m,&t)!=EOF)
        {
                if (n<m)  swap(n,m);
                memset(f,0,sizeof(f));
                f[0][0][0]=1;
                for (int i=1;i<=n;i++)
                        for (int r=0;r<=t;r++)
                                for (int j=0;j<(1<<m);j++) // 当前的状态 
                                {
                                        int num=getNum(j); // 棋子个数 
                                        if (num==-1 || num>r) continue;
                                        for (int k=0;k<(1<<m);k++) // 上次的状态转移 
                                        {
                                                if (getNum(k)==-1 || k&j) continue;
                                                f[i][j][r]+=f[i-1][k][r-num];
                                        }
                                }
                long long ans=0;
                for (int i=0;i<(1<<m);i++) ans+=f[n][i][t];
                printf("%lld\n",ans);
        }
        return 0;
}
-----------------------------------------------------------------------------------------------------
 
3、例题

分析:同样地,这道题用f[i][j][k]表示从(1,1)到(i,j)子矩阵中当所走路径状态为k的时候所得的最小权值,那么状态则是根据所走路径转换为二进制在转换为十进制来转移,就不多说了,看代码。

 
代码:
---------------------------------------------------------------------------------------------------
#include<cstdio>
#include<cstring>
#define MAXN 11
#define INF 0x3f3f3f3f
 
int min(int a,int b) { return (a<b)?a:b };
 
const int two[MAXN]={1,2,4,8,16,32,64,128,256,512};
int map[MAXN][MAXN],f[MAXN][MAXN][(1<<10)+1],ans,ret,tot;
 
int getNum(int ki,int now)
{
        int temp=ki;
        for (int i=9;i>=0;i--) if (temp>=two[i]) { temp-=two[i]; if (i==now) return ki; }
        return (ki+two[now]);
}
 
int main()
{
        memset(f,INF,sizeof(f));
        for (int i=1;i<=10;i++)
                for (int j=1;j<=10;j++) scanf("%d",&map[i][j]);
        int t1=two[map[1][1]],t2=0;
        f[1][1][t1]=map[1][1];
        for (int i=2;i<=10;i++) 
        {
                t2=getNum(t1,map[1][i]);
                f[1][i][t2]=f[1][i-1][t1]+map[1][i];
                t1=t2;
        }
        t1=two[map[1][1]];
        for (int i=2;i<=10;i++) 
        {
                t2=getNum(t1,map[i][1]);
                f[i][1][t2]=f[i-1][1][t1]+map[i][1];
                t1=t2;
        }
        for (int i=2;i<=10;i++)
                for (int j=2;j<=10;j++)
                {
                        for (int ki=1;ki<=(1<<10)-1;ki++)
                        {
                                if (f[i-1][j][ki]==INF) continue;
                                int k=getNum(ki,map[i][j]);
                                f[i][j][k]=min(f[i][j][k],f[i-1][j][ki]+map[i][j]);
                        }
                        for (int ki=1;ki<=(1<<10)-1;ki++)
                        {
                                if (f[i][j-1][ki]==INF) continue;
                                int k=getNum(ki,map[i][j]);
                                f[i][j][k]=min(f[i][j][k],f[i][j-1][ki]+map[i][j]);
                        }
         }
        for (int i=1;i<=1023;i++) if (f[3][3][i]!=INF) printf("i=%d %d\n",i,f[3][3][i]);
        printf("%d",f[10][10][1023]);
        return 0;
}
---------------------------------------------------------------------------------------------------

[知识点]状态压缩DP的更多相关文章

  1. hdu4336 Card Collector 状态压缩dp

    Card Collector Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...

  2. 浅谈状态压缩DP

    浅谈状态压缩DP 本篇随笔简单讲解一下信息学奥林匹克竞赛中的状态压缩动态规划相关知识点.在算法竞赛中,状压\(DP\)是非常常见的动规类型.不仅如此,不仅是状压\(DP\),状压还是很多其他题目的处理 ...

  3. hoj2662 状态压缩dp

    Pieces Assignment My Tags   (Edit)   Source : zhouguyue   Time limit : 1 sec   Memory limit : 64 M S ...

  4. POJ 3254 Corn Fields(状态压缩DP)

    Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4739   Accepted: 2506 Descr ...

  5. HDU-4529 郑厂长系列故事——N骑士问题 状态压缩DP

    题意:给定一个合法的八皇后棋盘,现在给定1-10个骑士,问这些骑士不能够相互攻击的拜访方式有多少种. 分析:一开始想着搜索写,发现该题和八皇后不同,八皇后每一行只能够摆放一个棋子,因此搜索收敛的很快, ...

  6. DP大作战—状态压缩dp

    题目描述 阿姆斯特朗回旋加速式阿姆斯特朗炮是一种非常厉害的武器,这种武器可以毁灭自身同行同列两个单位范围内的所有其他单位(其实就是十字型),听起来比红警里面的法国巨炮可是厉害多了.现在,零崎要在地图上 ...

  7. 状态压缩dp问题

    问题:Ignatius has just come back school from the 30th ACM/ICPC. Now he has a lot of homework to do. Ev ...

  8. BZOJ-1226 学校食堂Dining 状态压缩DP

    1226: [SDOI2009]学校食堂Dining Time Limit: 10 Sec Memory Limit: 259 MB Submit: 588 Solved: 360 [Submit][ ...

  9. Marriage Ceremonies(状态压缩dp)

     Marriage Ceremonies Time Limit:2000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu ...

随机推荐

  1. Android源码-学习随笔

    在线代码网站1:http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/ 书籍: ...

  2. Delphi的字符串、PChar和字符数组之间的转换

    参考:http://my.oschina.net/kavensu/blog/193719 以下的各种方法都是我在Delphi 6的环境下测试成功的,可能根据你的开发环境.不同的上下文语境……有一些可能 ...

  3. C结构体中数据的内存对齐问题

    转自:http://www.cnblogs.com/qwcbeyond/archive/2012/05/08/2490897.html 32位机一般默认4字节对齐(32位机机器字长4字节),64位机一 ...

  4. WPF线程(Step1)——Dispatcher

    使用WPF开发时经常会遇上自己建立的线程需要更新界面UI内容,从而导致的跨线程问题. 异常内容: 异常类型:System.InvalidOperationException 异常描述: "S ...

  5. 使用Jmeter进行http接口性能测试

    在进行网页或应用程序后台接口开发时,一般要及时测试开发的接口能否正确接收和返回数据,对于单次测试,Postman插件是个不错的Http请求模拟工具. 但是Postman只能模拟单客户端的单次请求,而对 ...

  6. Oracle中“行转列”的实现方式

    在报表的开发当中,难免会遇到行转列的问题. 以Oracle中scott的emp为例,统计各职位的人员在各部门的人数分布情况,就可以用“行转列”: scott的emp的原始数据为: EMPNO ENAM ...

  7. 基于XMPP协议的Android即时通信系

    以前做过一个基于XMPP协议的聊天社交软件,总结了一下.发出来. 设计基于开源的XMPP即时通信协议,采用C/S体系结构,通过GPRS无线网络用TCP协议连接到服务器,以架设开源的Openfn'e服务 ...

  8. Hadoop RPC机制的使用

    一.RPC基础概念 1.1 RPC的基础概念 RPC,即Remote Procdure Call,中文名:远程过程调用: (1)它允许一台计算机程序远程调用另外一台计算机的子程序,而不用去关心底层的网 ...

  9. host_network_interfaces_slow_mode_thresholds

    CDH报集群异常: host_network_interfaces_slow_mode_thresholds 警告置为从不-- 主要是我没有查到发生这种情况的原因是什么--

  10. 【转】最近搞Hadoop集群迁移踩的坑杂记

    http://ju.outofmemory.cn/entry/237491 Overview 最近一段时间都在搞集群迁移.最早公司的hadoop数据集群实在阿里云上的,机器不多,大概4台的样子,据说每 ...