【插头DP】BZOJ1187- [HNOI2007]神奇游乐园

【题目大意】

在n*m的网格中选一条回路,使权值和最大。

【思路】

和之前裸的插头DP差不多,只不过现在回路不需要经过所有的格子。所以有以下几个注意点(具体看注释):

(1)left和up插头相等的时候,直接更新答案;

(2)left和up插头不存在的时候,还要考虑当前格子不取的情况。

orz写了半天,感觉被掏空.jpg

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 typedef long long ll;
  4 const int MAXN=10;
  5 const int MAXM=105;
  6 const int HASH=30007;
  7 const int INF=0x7fffffff;
  8 ll ans=-1ll << 60;
  9 int n,m,sat[MAXM][MAXN];
 10 int code[MAXN];
 11 
 12 struct HashMap
 13 {
 14     vector<int> hash[HASH];
 15     vector<ll> state,f;
 16     
 17     void clear()
 18     {
 19         for(int i=0;i<HASH;i++) vector<int>().swap(hash[i]);
 20         vector<ll>().swap(state);
 21         vector<ll>().swap(f);
 22     }
 23     
 24     void push(ll st,ll ans)
 25     {
 26         int h=st%HASH;
 27         for (int i=0;i<hash[h].size();i++)
 28         {
 29             int now=hash[h][i];
 30             if (state[now]==st)
 31             {
 32                 f[now]=max(f[now],ans);
 33                 return;
 34             }
 35         }
 36         state.push_back(st);
 37         f.push_back(ans);
 38         hash[h].push_back(state.size()-1);
 39     }
 40 }dp[2];
 41 
 42 void decode(ll state)
 43 {
 44     memset(code,0,sizeof(code));
 45     for (int i=n;i>=0;i--)
 46     {
 47         code[i]=state&7;
 48         state>>=3;
 49     }
 50 }
 51 
 52 void shift()
 53 {
 54     for (int i=n;i>0;i--) code[i]=code[i-1];
 55     code[0]=0;
 56 } 
 57 
 58 ll encode()
 59 {
 60     int ch[MAXN],cnt=0;
 61     memset(ch,-1,sizeof(ch));
 62     ch[0]=0;
 63     ll ret=0;
 64     for (int i=0;i<=n;i++)
 65     {
 66         if (ch[code[i]]==-1) ch[code[i]]=++cnt;
 67         code[i]=ch[code[i]];
 68         ret<<=3;
 69         ret|=code[i];
 70     }
 71     return ret;
 72 }
 73 
 74 void dpblank(int i,int j,int cur)
 75 {
 76     for (int k=0;k<dp[1-cur].state.size();k++)
 77     {
 78         decode(dp[1-cur].state[k]);
 79         if (j==1)//把之前shift的写法改成这样写了,总之就过了,也不知道为什么orz 
 80         {
 81             if (code[n]) continue;
 82             shift();
 83         }
 84         int left=code[j-1],up=code[j];
 85         
 86         if (left && up)
 87         {
 88             code[j-1]=code[j]=0;
 89             if (left==up)
 90             {
 91                 if (encode()==0) ans=max(ans,dp[1-cur].f[k]+sat[i][j]);
 92                 //注意和之前不一样的地方:如果已经封闭起来了就更新答案 
 93             }
 94             else
 95             {
 96                 for (int t=0;t<=n;t++) if (code[t]==left) code[t]=up;
 97                 dp[cur].push(encode(),dp[1-cur].f[k]+sat[i][j]);
 98             }
 99         }
100         
101         if ((left && (!up)) || (up && (!left)))
102         {
103             int t=left|up;
104             code[j-1]=0;
105             code[j]=t;
106             dp[cur].push(encode(),dp[1-cur].f[k]+sat[i][j]);             
107             code[j-1]=t;
108             code[j]=0;
109             dp[cur].push(encode(),dp[1-cur].f[k]+sat[i][j]);
110         }
111         
112         if (!left && !up)
113         { 
114             dp[cur].push(encode(),dp[1-cur].f[k]);
115             //注意并不一定是所有格子都要经过,所以还要考虑不经过当前格子的情况 
116             code[j-1]=code[j]=MAXN-1;
117             dp[cur].push(encode(),dp[1-cur].f[k]+sat[i][j]);
118         }
119     }
120 }
121 
122 void init()
123 {
124     scanf("%d%d",&m,&n);
125     for (int i=1;i<=m;i++)
126         for (int j=1;j<=n;j++) scanf("%d",&sat[i][j]);
127 }
128 
129 void solve()
130 {
131     int cur=0;
132     dp[cur].clear();
133     dp[cur].push(0,0);
134     for (int i=1;i<=m;i++)
135         for (int j=1;j<=n;j++)
136         {
137             cur^=1;
138             dp[cur].clear();
139             dpblank(i,j,cur);
140         }
141     printf("%lld",ans);
142 }
143 
144 int main()
145 {
146     init();
147     solve();
148     return 0;
149 }

 

posted @ 2016-10-01 14:02  iiyiyi  阅读(420)  评论(0编辑  收藏  举报