Description
Hnoi2007-Day1有一道题目 Park:给你一个 m * n 的矩阵,每个矩阵内有个
权值V(i,j) (可能为负数),要求找一条回路,使得每个点最多经过一次,并且经过
的点权值之和最大,想必大家印象深刻吧.
无聊的小 C 同学把这个问题稍微改了一下:要求找一条路径,使得每个点
最多经过一次,并且点权值之和最大,如果你跟小 C 一样无聊,就麻烦做一下
这个题目吧.
30%的数据,n≤6.
100%的数据,m<=100,n ≤ ≤8.
注意:路径上有可能只有一个点.
Solution
讲个鬼故事,插头dp
对于非闭合回路的问题我们可以添加一类新状态——单身插头(雾,单身插头表示的是一条路径在轮廓线上的一个端点而非一对括号中的一个,因此没有括号与之配对并且不参与括号配对。如果出现了2个单身插头或者有且仅有1个单身插头更新答案就行了。并且由单身插头的定义可知,轮廓线上不可能出现超过2个单身插头。
含有单身插头的转移包括:
上/左无插头,可以新建向右/向下单身插头
上/左中一侧有非单身插头,可以把这个插头变成路径的一端,原本的另一端变成单身插头
上/左中一侧有单身插头,可以把插头延续或转弯
上/左中一侧有单身插头,另一侧有非单身插头,那么连起来并把非单身插头的另一端变成单身插头
可能说得不清楚,具体看代码
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
int f[102][10][8400],bin[10],rc[105][9];
int rec[8400],rnk[262144],tot,n,m;
int mx;
int get(int S,int x) {
return (S/bin[x])%4;
}
void update(int &x,int v) {
(x<v)?(x=v):0;
}
void dfs(int dep,int S,int sum,int cnt) {
if (cnt>2||sum<0||sum+dep-1>m) return ;
if (dep>m) {
rec[++tot]=S;
rnk[S]=tot;
return ;
}
dfs(dep+1,S,sum,cnt);
dfs(dep+1,S+bin[dep],sum+1,cnt);
dfs(dep+1,S+2*bin[dep],sum-1,cnt);
dfs(dep+1,S+3*bin[dep],sum,cnt+1);
}
int rig(int S,int x) {
for (int i=x,cnt=0;i<=m;++i) {
int tmp=get(S,i);
if (tmp==1) cnt++;
else if (tmp==2) cnt--;
if (!cnt) return bin[i];
}
return -1;
}
int lef(int S,int x) {
for (int i=x,cnt=0;~i;--i) {
int tmp=get(S,i);
if (tmp==1) cnt++;
else if (tmp==2) cnt--;
if (!cnt) return bin[i];
}
return -1;
}
int main(void) {
// freopen("data.in","r",stdin);
bin[0]=1; rep(i,1,9) bin[i]=bin[i-1]*4;
int ans=0; scanf("%d%d",&n,&m);
dfs(0,0,0,0);
rep(i,1,n) rep(j,1,m) scanf("%d",&rc[i][j]);
// if (n<m) std:: swap(n,m);
fill(f,-31); f[1][0][rnk[0]]=0;
rep(i,1,n) {
rep(j,1,m) {
update(ans,rc[i][j]);
rep(k,1,tot) {
int S=rec[k],p=get(S,j-1),q=get(S,j);
int tmp=f[i][j-1][k]+rc[i][j];
if (!p&&!q) {
update(f[i][j][rnk[S+bin[j-1]+2*bin[j]]],tmp);
update(f[i][j][rnk[S+3*bin[j-1]]],tmp);
update(f[i][j][rnk[S+3*bin[j]]],tmp);
update(f[i][j][k],f[i][j-1][k]);
}
if (!p&&q==1) {
update(f[i][j][k],tmp);
update(f[i][j][rnk[S-bin[j]+bin[j-1]]],tmp);
update(f[i][j][rnk[S-bin[j]+rig(S,j)]],tmp);
}
if (!p&&q==2) {
update(f[i][j][k],tmp);
update(f[i][j][rnk[S-2*bin[j]+2*bin[j-1]]],tmp);
update(f[i][j][rnk[S-2*bin[j]+2*lef(S,j)]],tmp);
}
if (!p&&q==3) {
update(f[i][j][k],tmp);
update(f[i][j][rnk[S-3*bin[j]+3*bin[j-1]]],tmp);
if (S==q*bin[j]) update(ans,tmp);
}
if (p==1&&!q) {
update(f[i][j][k],tmp);
update(f[i][j][rnk[S-bin[j-1]+bin[j]]],tmp);
update(f[i][j][rnk[S-bin[j-1]+rig(S,j-1)]],tmp);
}
if (p==2&&!q) {
update(f[i][j][k],tmp);
update(f[i][j][rnk[S-2*bin[j-1]+2*bin[j]]],tmp);
update(f[i][j][rnk[S-2*bin[j-1]+2*lef(S,j-1)]],tmp);
}
if (p==3&&!q) {
update(f[i][j][k],tmp);
update(f[i][j][rnk[S-3*bin[j-1]+3*bin[j]]],tmp);
if (S==p*bin[j-1]) update(ans,tmp);
}
if (p==1&&q==1) update(f[i][j][rnk[S-bin[j]-bin[j-1]-rig(S,j)]],tmp);
if (p==1&&q==2&&S==bin[j-1]+2*bin[j]) update(ans,tmp);
if (p==1&&q==3) update(f[i][j][rnk[S-3*bin[j]-bin[j-1]+rig(S,j-1)]],tmp);
if (p==2&&q==1) update(f[i][j][rnk[S-2*bin[j-1]-bin[j]]],tmp);
if (p==2&&q==2) update(f[i][j][rnk[S-2*bin[j-1]-2*bin[j]+lef(S,j-1)]],tmp);
if (p==2&&q==3) update(f[i][j][rnk[S-3*bin[j]-2*bin[j-1]+2*lef(S,j-1)]],tmp);
if (p==3&&q==1) update(f[i][j][rnk[S-3*bin[j-1]-bin[j]+rig(S,j)]],tmp);
if (p==3&&q==2) update(f[i][j][rnk[S-3*bin[j-1]-2*bin[j]+2*lef(S,j)]],tmp);
if (p==3&&q==3&&S==q*bin[j]+p*bin[j-1]) update(ans,tmp);
}
}
if (i!=n) rep(k,1,tot) if (rec[k]%4==0) update(f[i+1][0][k],f[i][m][rnk[rec[k]/4]]);
}
printf("%d\n", ans);
return 0;
}