题目大意
给定一个m行n列的矩阵,你可以从任意位置开始取数,到达任意位置都可以结束,每次可以走到的数是当前这个数上下左右的邻居之一,唯一的限制是每个位置只能经过一次,也就是说你的路径不自交。所经过的数的总作为你的得分,求最大的得分。
n,m≤10
整数范围[-10000000,10000000]
分析
求一条路径、上下左右走的矩阵,很容易想到插头DP。
设f[i][j][S]表示决策到(i,j),插头状态为S的答案。现在看状态怎样存。
首先无插头表示为0,联通的插头看成左右括号,记为1、2,由于是一条不自交的路径,括号序是没问题的。路径会有两个端点,这个端点伸出来的插头不与其它插头配对(除非在当前决策的格子与其它插头相连),所以还要记录独立的插头,记为3
这样就可以4进制来表示了。
然后就可以分类讨论了!
注意边界!!!
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=209189;
typedef long long LL;
int n,m,p,q,cnt[1<<22],ans;
int map[11][11];
struct node
{
int H[maxn],tot,st[maxn];
LL s[maxn];
void init()
{
memset(H,255,sizeof(H));
tot=0;
}
void add(int S,int v)
{
int i=S%maxn;
for (;H[i]>=0 && st[H[i]]!=S;i=(i+1)%maxn);
if (H[i]<0)
{
st[tot]=S; s[tot]=v; H[i]=tot++;
}else if (v>s[H[i]]) s[H[i]]=v;
}
}f[2];
int Get(int s,int w)
{
return (s>>(w<<1))&3;
}
void Set(int &s,int w,int v)
{
s^=Get(s,w)<<(w<<1);
s^=v<<(w<<1);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=0;i<n;i++) for (int j=0;j<m;j++) scanf("%d",&map[i][j]);
for (int i=0;i<(1<<(m*2+2));i++)
for (int j=0;j<m;j++) cnt[i]+=(Get(i,j)==3);
p=0; q=1;
f[p].init();
f[p].add(0,0);
ans=-10000001;
for (int i=0;i<n;i++) for (int j=0;j<m;j++) ans=max(ans,map[i][j]);
for (int i=0;i<n;i++)
{
for (int j=0;j<m;j++,p^=