码迷,mamicode.com
首页 > 其他好文 > 详细

插头dp

时间:2016-07-08 21:48:52      阅读:351      评论:0      收藏:0      [点我收藏+]

标签:

插头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&&BZOJ3753

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

还没写QAQ

插头dp

标签:

原文地址:http://www.cnblogs.com/wjyi/p/5654500.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!