【BZOJ】2310: ParkII 插头DP
【题意】给定m*n的整数矩阵,求经过所有点至多一次路径的最大数值和。n<=8,m<=100。
【算法】插头DP
【题解】最小表示法确实十分通用,处理简单路径问题只需要状态多加一位表示独立插头的数量0~2(即路径端点),转移的时候多考虑凭空产生独立插头和结尾为独立插头的情况即可。
可以跳格的情况直接转移就行。求最值路径和求路径数的区别就是把加改成乘。这样每行至多5种联通编号,用8进制即可。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int maxn=10,MOD=10007,S=3000010; int n,m,map[110][maxn],c[maxn],num; struct h{ int first[MOD],nxt[S],state[S],tot;// ll ans[S]; void init(){ memset(first,0,sizeof(first)); tot=0; } void insert(int x,ll num){ for(int i=first[x%MOD];i;i=nxt[i]){ if(state[i]==x){ ans[i]=max(ans[i],num); return; } } state[++tot]=x;ans[tot]=num; nxt[tot]=first[x%MOD];first[x%MOD]=tot; } }f[2]; void decode(int x){num=x&7;x>>=3;for(int i=m;i>=0;i--)c[i]=x&7,x>>=3;} int vis[10];// int encode(){ for(int i=1;i<=6;i++)vis[i]=0; int cnt=0,x=0; for(int i=0;i<=m;i++){ if(!c[i]){x<<=3;continue;} if(!vis[c[i]])vis[c[i]]=++cnt; x=(x<<3)|vis[c[i]]; } return x=(x<<3)|num; } bool o(int x,int y){return x<=n&&y<=m;} void solve(int cur,int x,int y){ for(int k=1;k<=f[cur^1].tot;k++){ decode(f[cur^1].state[k]); int left=c[y-1],up=c[y];ll ans=f[cur^1].ans[k]+map[x][y]; if(left&&up){ if(left!=up){ c[y-1]=c[y]=0; for(int i=0;i<=m;i++)if(c[i]==left)c[i]=up; f[cur].insert(encode(),ans); } } else if(left||up){ int now=left^up; if(o(x+1,y)){ c[y-1]=now;c[y]=0; f[cur].insert(encode(),ans); } if(o(x,y+1)){ c[y-1]=0;c[y]=now; f[cur].insert(encode(),ans); } if(num<2){ num++;c[y-1]=c[y]=0; f[cur].insert(encode(),ans); } } else{ f[cur].insert(encode(),f[cur^1].ans[k]); if(o(x+1,y)&&o(x,y+1)){ c[y-1]=c[y]=6; f[cur].insert(encode(),ans); } if(num<2){ num++; if(o(x+1,y)){ c[y-1]=6;c[y]=0; f[cur].insert(encode(),ans); } if(o(x,y+1)){ c[y-1]=0;c[y]=6; f[cur].insert(encode(),ans); } } } } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&map[i][j]); int cur=0;f[0].init();f[0].insert(0,0); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ f[cur^=1].init();solve(cur,i,j); } for(int j=1;j<=f[cur].tot;j++){ int num=f[cur].state[j]&7; f[cur].state[j]=((f[cur].state[j]>>6)<<3)|num;// } } ll ANS=-1ll<<60; for(int i=1;i<=f[cur].tot;i++){ if((f[cur].state[i]&7)==2)ANS=max(ANS,f[cur].ans[i]); } printf("%lld",ANS); return 0; }
注意桶数组vis开成int类型,奇怪的问题一定是数组空间的问题。