wjyi

人这么弱,有什么资格颓废(ಥ _ ಥ)

  博客园  :: 首页  :: 新随笔  :: 联系 ::  :: 管理

插头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;
}

  

posted on 2016-07-08 20:27  wjyi  阅读(478)  评论(0编辑  收藏  举报