插头dp

感受:

我觉得重点是理解,算法并不是直接想出怎样由一种方案变成另一种方案。而是方案本来就在那里,我们只是枚举状态统计了答案。

看看cdq的讲义什么的,一开始可能觉得状态很多,但其实灰常简单

就像lyd说的,考插头dp的题目就是在考模板2333

(学这个之前连hash_map都没写过2333

WA:

(1) 初始化矩阵,周围格子有可能是0--->转移出错

(2)统计答案最后统计的是合法的,即st==0的。。。

题目集锦:

(1)cojs1512 经过所有可经过的点的一条回路个数

因为是一条回路,依次dp每个点的状态,所以记录endx,endy只在终点更新答案,其它点的闭合回路不计算。

#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
#define mod 13131
#define N 4500
#define ll long long
struct dp_hash{
int head[mod],next[N],sz;
ll f[N],st[N];
void init(){
memset(head,0,sizeof(head));sz=0;
}
void push(ll S,ll ins){
int now=S%mod;
for(int i=head[now];i;i=next[i])
if(st[i]==S){
f[i]+=ins;return;
}
sz++;
f[sz]=ins;st[sz]=S;
next[sz]=head[now];
head[now]=sz;
}
}dp[2];
ll ans=0;
int n,m,code[16],ch[16],a[16][16],cur,Endx,Endy;
char s[15];
void Decode(ll S){
for(int i=m;i>=0;i--){
code[i]=S&7;
S>>=3;
}
}
ll Encode(){
ll S=0;
memset(ch,-1,sizeof(ch));int cnt=0;
ch[0]=0;
for(int i=0;i<=m;i++){
if(ch[code[i]]==-1)ch[code[i]]=++cnt;
code[i]=ch[code[i]];
S<<=3;S|=code[i];
}
return S;
}
void Shift(){
for(int i=m;i>0;i--)code[i]=code[i-1];
code[0]=0;
}
void DP(int x,int y,int pre,bool type){
if(!type){
for(int k=1;k<=dp[pre].sz;k++){
Decode(dp[pre].st[k]);
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[k]);
}
return;
}
for(int k=1;k<=dp[pre].sz;k++){
Decode(dp[pre].st[k]); int Left=code[y-1],Up=code[y];
if(Left&&Up){
if(Left==Up){
if(Endx==x&&Endy==y)
ans+=dp[cur].f[k];
}
else{
code[y]=code[y-1]=0;
for(int i=0;i<=m;i++)
if(code[i]==Up)code[i]=Left;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[k]);
}
}
else if(Left==0&&Up==0){
if(a[x][y+1]&&a[x+1][y]){
code[y-1]=code[y]=15;
dp[pre^1].push(Encode(),dp[pre].f[k]);
}
}
else{
int tmp=Left==0?Up:Left;
if(a[x][y+1]){
code[y-1]=0;code[y]=tmp;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[k]);
}
if(a[x+1][y]){
code[y-1]=tmp;code[y]=0;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[k]);
}
}
}
}
int main(){
freopen("formula1.in","r",stdin);
freopen("formula1.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++)
if(s[j]!='*'){
a[i][j]=1;
Endx=i;Endy=j;
}
} if(Endx==0){
puts("0");return 0;
}
cur=0;
dp[cur].init();
dp[cur].push(0,1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
dp[cur^1].init();
DP(i,j,cur,a[i][j]);
cur^=1;
}
printf("%lld\n",ans);
return 0;
}

(2) hdu1693 Eat The Trees

经过所有非障碍点的回路个数(不限条数)。

和上一道题的区别就是非终点的回路也要更新其它状态。

#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
#define N 1000000
#define mod 13131
#define ll long long
struct dphash{
int head[N],next[N],sz;
ll st[N],f[N];
void init(){
memset(head,0,sizeof(head));sz=0;
}
void push(ll S,ll ins){
int now=S%mod;
for(int i=head[now];i;i=next[i])
if(st[i]==S){
f[i]+=ins;return;
}
sz++;
st[sz]=S;f[sz]=ins;
next[sz]=head[now];head[now]=sz;
}
}dp[2];
int n,m,T,cur,pw[16],a[16][16],code[16];
ll ans;
ll Encode(){
ll S=0;
memset(pw,-1,sizeof(pw));
pw[0]=0;int cnt=0;
for(int i=0;i<=m;i++){
if(pw[code[i]]==-1)pw[code[i]]=++cnt;
code[i]=pw[code[i]];
S<<=3;S|=code[i];
}
return S;
}
void Decode(ll S){
for(int i=m;i>=0;i--){
code[i]=S&7;
S>>=3;
}
}
void Shift(){
for(int i=m;i;i--)code[i]=code[i-1];
code[0]=0;
}
void DP(int x,int y,int pre,bool type){
if(!type){
for(int i=1;i<=dp[pre].sz;i++){
Decode(dp[pre].st[i]);
// cout<<"code "<<code[y]<<' '<<code[y-1]<<endl;
code[y]=code[y-1]=0;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
return;
}
for(int i=1;i<=dp[pre].sz;i++){
Decode(dp[pre].st[i]); int Left=code[y-1],Up=code[y];
if(Left&&Up){
code[y]=code[y-1]=0;
for(int j=0;j<=m;j++)if(code[j]==Up)code[j]=Left;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
else if(Left==0&&Up==0){
if(a[x][y+1]&&a[x+1][y]){
code[y-1]=code[y]=15;
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
}
else{
int tmp=Left==0?Up:Left;
if(a[x][y+1]){
code[y]=tmp;code[y-1]=0;
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
if(a[x+1][y]){
code[y-1]=tmp;code[y]=0;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
}
}
}
int main(){
// freopen("1693.in","r",stdin);
// freopen("1693.out","w",stdout);
T=read();
for(int k=1;k<=T;k++){
memset(a,0,sizeof(a));
n=read();m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)a[i][j]=read();
cur=0;
dp[cur].init();
dp[cur].push(0,1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
dp[cur^1].init();
DP(i,j,cur,a[i][j]);
cur^=1;
}
ans=0;
for(int i=dp[cur].head[0];i;i=dp[cur].next[i])if(dp[cur].st[i]==0)ans+=dp[cur].f[i];
printf("Case %d: There are %I64d ways to eat the trees.\n",k,ans);
}
return 0;
}

  

(3)[国家集训队2011]画圈圈

根据射线法,判断一个点左边的下插头奇偶性判断是否在回路内。

#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
#define ll long long
#define N 1000000
#define mod 13131
#define MOD 123456791
struct dp_hash{
int head[mod],next[N],sz;
ll f[N],st[N];
void init(){
memset(head,0,sizeof(head));sz=0;
}
void push(ll S,ll ins){
ins%=MOD;int now=S%mod;
for(int i=head[now];i;i=next[i])
if(st[i]==S){
(f[i]+=ins)%=MOD;return;
}
sz++;next[sz]=head[now];head[now]=sz;
st[sz]=S;f[sz]=ins;
}
}dp[2];
int n,m,cur,pw[16],a[25][16],code[16];
ll ans;
char s[16];
void Decode(ll S){
for(int i=m;i>=0;i--){
code[i]=S&7;
S>>=3;
}
}
ll Encode(){
ll S=0;
memset(pw,-1,sizeof(pw));
pw[0]=0;int cnt=0;
for(int i=0;i<=m;i++){
if(pw[code[i]]==-1)pw[code[i]]=++cnt;
code[i]=pw[code[i]];
S<<=3;S|=code[i];
}
return S;
}
void Shift(){
for(int i=m;i;i--)code[i]=code[i-1];
code[0]=0;
}
void DP(int x,int y,int pre,int type){
if(type){
for(int i=1;i<=dp[pre].sz;i++){
Decode(dp[pre].st[i]);int t=0;
for(int j=0;j<y-1;j++)if(code[j]!=0)t++;
if((t%2==1&&type==1)||(t%2==0&&type==2)){
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
}
return;
} for(int i=1;i<=dp[pre].sz;i++){
Decode(dp[pre].st[i]); int Left=code[y-1],Up=code[y];
if(Left&&Up){
code[y]=code[y-1]=0;
for(int j=0;j<=m;j++)if(code[j]==Up)code[j]=Left;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
else if(Left==0&&Up==0){
if(a[x][y+1]==0&&a[x+1][y]==0){
code[y-1]=code[y]=15;
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
}
else{
int tmp=Left==0?Up:Left;
if(a[x][y+1]==0){
code[y]=tmp;code[y-1]=0;
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
if(a[x+1][y]==0){
code[y-1]=tmp;code[y]=0;
if(y==m)Shift();
dp[pre^1].push(Encode(),dp[pre].f[i]);
}
}
}
}
int main(){
freopen("nt2011_circle.in","r",stdin);
freopen("nt2011_circle.out","w",stdout);
n=read();m=read();
memset(a,-1,sizeof(a));
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++)
if(s[j]=='.')a[i][j]=0;
else if(s[j]=='*')a[i][j]=1;
else a[i][j]=2;
}
cur=0;
dp[cur].init();
dp[cur].push(0,1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
dp[cur^1].init();
DP(i,j,cur,a[i][j]);
cur^=1;
}
for(int i=dp[cur].head[0];i;i=dp[cur].next[i])if(dp[cur].st[i]==0){ans=dp[cur].f[i];break;}
printf("%lld\n",ans);
}

(4)BZOJ1187

都是把记录方案个数的变量改为记录当前状态得到的权值

注意的是每个状态现在要记录的是最大值,就是说一个状态可以对应多种可能,你只有一个轮廓线,但轮廓线上方是怎么样的无法确定,所以要取ma]x

还有就是更新答案的时候,要code[y]=code[y-1]=0,然后判断Encode()==0,否则的话有可能两边还有插头,这样下面还会更新一次。

边界什么的也比较麻烦,容易写漏。

(5) BZOJ3753

这道我感觉超难的qaq,注意dp的是矩阵的边,但权值还是格子。。然后就会发现不会转移,奥妙重重。。

smz原来教过我,然而我忘了QAQ

然后她现在人在衡水QAQ不上QQ QAQ

问了大爷。。还没回复QAQ

还没写QAQ

update 前几天终于写了,这个其实可以理解为是否放置了下插头

首先就是n++,m++,然后选择一条边(即新图的一个格子),如果它有下插头,才能对当前格子是否在圈内产生影响。

贴代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
return x*f;
}
#define mod 13131
#define N 1000000
#define inf 1e9
int n,m,code[20],a[15][15],v[15][15],num=0,ans=-inf;
struct hash_map{
int head[mod],next[N],sz,f[N];
ll st[N];
void init(){
memset(head,0,sizeof(head));sz=0;
}
void push(ll S,int ins){
int now=S%mod;
for(int i=head[now];i;i=next[i])
if(st[i]==S){
f[i]=max(f[i],ins);return;
}
st[++sz]=S;f[sz]=ins;
next[sz]=head[now];head[now]=sz;
}
}dp[2];
int pw[20];
ll Encode(){
memset(pw,-1,sizeof(pw));
pw[0]=0;ll S=0;int cnt=0;
for(int i=0;i<=m;i++){
if(pw[code[i]]==-1)pw[code[i]]=++cnt;
code[i]=pw[code[i]];
S=S<<3|code[i];
}
return S;
}
void Decode(ll S){
for(int i=m;i>=0;i--)
code[i]=S&7,S>>=3;
}
void Shift(){
for(int i=m;i;i--)code[i]=code[i-1];
code[0]=0;
}
bool check(int x,int y,bool is){
if(a[x][y]==1&&is)return 0;
if(a[x][y]==2&&is==0)return 0;
return 1;
}
void DP(int x,int y,int pre){
for(int i=1;i<=dp[pre].sz;i++){
Decode(dp[pre].st[i]);
if(y==1){if(code[m])continue;Shift();}
int Left=code[y-1],Up=code[y];
bool is=0;
for(int j=0;j<y-1;j++)if(code[j])is^=1;
if(Left&&Up){
code[y]=code[y-1]=0;
if(Left==Up){
if(Encode()==0&&num==0)ans=max(ans,dp[pre].f[i]);
}
else{
if(check(x,y,is)){
for(int j=0;j<=m;j++)if(code[j]==Up)code[j]=Left;
dp[pre^1].push(Encode(),dp[pre].f[i]+is*v[x][y]);
}
}
}
else if(Left||Up){
int tmp=Left?Left:Up;
if(y!=m&&check(x,y,is)){
code[y-1]=0;code[y]=tmp;
dp[pre^1].push(Encode(),dp[pre].f[i]+is*v[x][y]);
}
is^=1;
if(x!=n&&check(x,y,is)){
code[y-1]=tmp;code[y]=0;
dp[pre^1].push(Encode(),dp[pre].f[i]+is*v[x][y]);
}
}
else{
if(check(x,y,is))dp[pre^1].push(Encode(),dp[pre].f[i]+is*v[x][y]);
is^=1;
if(x!=n&&y!=m&&check(x,y,is)){
code[y]=code[y-1]=15;
dp[pre^1].push(Encode(),dp[pre].f[i]+is*v[x][y]);
}
}
}
}
int main(){
// freopen("test.in","r",stdin);
n=read();m=read();n++;m++;
for(int i=1;i<n;i++)
for(int j=1;j<m;j++)v[i][j]=read();
int cur=0;dp[cur].init();dp[cur].push(0,0);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
dp[cur^1].init();
DP(i,j,cur);
cur^=1;
}
printf("%d\n",ans);
for(int i=1;i<n;i++)
for(int j=1;j<m;j++){
a[i][j]=read();
if(a[i][j]==2)num++;
}
cur=0;ans=-inf;
dp[cur].init();dp[cur].push(0,0);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
dp[cur^1].init();
DP(i,j,cur);
cur^=1;
if(a[i][j]==2)num--;
}
if(ans==-inf)printf("Can not establish GFW.");
else printf("%d\n",ans);
return 0;
}

  

插头dp的更多相关文章

  1. HDU 4113 Construct the Great Wall(插头dp)

    好久没做插头dp的样子,一开始以为这题是插头,状压,插头,状压,插头,状压,插头,状压,无限对又错. 昨天看到的这题. 百度之后发现没有人发题解,hust也没,hdu也没discuss...在acm- ...

  2. HDU 4949 Light(插头dp、位运算)

    比赛的时候没看题,赛后看题觉得比赛看到应该可以敲的,敲了之后发现还真就会卡题.. 因为写完之后,无限TLE... 直到后来用位运算代替了我插头dp常用的decode.encode.shift三个函数以 ...

  3. 插头DP专题

    建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议 ...

  4. HDU 1693 Eat the Trees(插头DP、棋盘哈密顿回路数)+ URAL 1519 Formula 1(插头DP、棋盘哈密顿单回路数)

    插头DP基础题的样子...输入N,M<=11,以及N*M的01矩阵,0(1)表示有(无)障碍物.输出哈密顿回路(可以多回路)方案数... 看了个ppt,画了下图...感觉还是挺有效的... 参考 ...

  5. HDU 1693 Eat the Trees(插头DP)

    题目链接 USACO 第6章,第一题是一个插头DP,无奈啊.从头看起,看了好久的陈丹琦的论文,表示木看懂... 大体知道思路之后,还是无法实现代码.. 此题是插头DP最最简单的一个,在一个n*m的棋盘 ...

  6. HDU 4064 Carcassonne(插头DP)(The 36th ACM/ICPC Asia Regional Fuzhou Site —— Online Contest)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4064 Problem Description Carcassonne is a tile-based ...

  7. URAL 1519 基础插头DP

    题目大意: 给定一个图,一部分点'*'作为障碍物,求经过所有非障碍点的汉密尔顿回路有多少条 基础的插头DP题目,对于陈丹琦的论文来说我觉得http://blog.sina.com.cn/s/blog_ ...

  8. uva 11270 - Tiling Dominoes(插头dp)

    题目链接:uva 11270 - Tiling Dominoes 题目大意:用1∗2木块将给出的n∗m大小的矩阵填满的方法总数. 解题思路:插头dp的裸题,dp[i][s]表示第i块位置.而且该位置相 ...

  9. bzoj 1187: [HNOI2007]神奇游乐园 插头dp

    1187: [HNOI2007]神奇游乐园 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 668  Solved: 337[Submit][Statu ...

随机推荐

  1. IIS GZip

    优点:提高网页响应速度(静态压缩会占用一定的存储空间,但是速度快,而动态压缩不占用存储空间,但是占用CPU时间,而且压缩比不恒定.) 缺点:动态压缩会影响CPU性能. win7:设置: iis管理器- ...

  2. Ubuntu14.04 64位配置Caffe 教程(基于CUDA7.5)

    最新博客地址已转到: http://blog.csdn.net/zzlyw?viewmode=contents   ------------------------------------------ ...

  3. dex文件格式三

    先来看看整体的结构,结构体定义在DexFile.h里面   在dexFileSetupBasicPointers中设置各个子结构体,当然是在解析DexHeader之后 源码在DexFile.c文件中 ...

  4. 高性能MySQL(四):schema陷阱

    一.schema陷阱 二.缓存表和汇总表 三.范式和反范式

  5. Selenium Xpath Tutorials - Identifying xpath for element with examples to use in selenium

    Xpath in selenium is close to must required. XPath is element locator and you need to provide xpath ...

  6. 高德地图纯js和html

    <!doctype html> <html> <head> <meta content="" charset="utf-8&qu ...

  7. 深入HashMap

    HashMap: 内部基于数组和单向链表 重要的变量有: Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE:结点数组table中存 ...

  8. Ubuntu安装Mysqlcluster集群

    可参考:http://xuwensong.elastos.org/2014/01/13/ubuntu-%E4%B8%8Bmysql-cluster%E5%AE%89%E8%A3%85%E5%92%8C ...

  9. javase-->基础知识(三)

    1.方法 普通方法:4个必要,1个可选 1):必须有返回值类型,没有返回值用void表示 2):必须有名字 3):必须有()和形参 4):必须有{}方法体 5):可选static,表示静态的方法,可以 ...

  10. octave手册

    GNU OCTAVE是一种高级语言,主要用于数值计算.它提供交互式命令行窗口,用于求解线性和非线性问题并计算出数值,并可以进行其它数值实验,还可以用来作为一个批量数据处理语言 运行Ocatve: oc ...