题目描述
【题目描述】
给你一个 n * m 的矩阵,矩阵中的每个格子都有一个分数,求从左上角走到右下角的最大分数,每个格子只能经过一次,也可以不经过。
如图,一个 3*3 的矩阵,最大分数为61 。
【输入】
多组数据,每组数据的第一行有两个整数 n 和 m(1<= n <=8,1<= m <=9) ,下面给出一个 n * m 的矩阵,矩阵中每个分数的值在 -2000 到 2000之间。
【输出】
对于第 i 组数据,输出 Case i: ans ,ans 为最大分数。
【样例输入】
2 2
1 2
3 1
3 3
0 -20 100
1 -20 -20
1 1 1
【样例输出】
Case 1: 5
Case 2: 61
其实和裸题也差不多。。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
const int mod=100037;
int n,m,map[15][15],now=0,list[2][mod],num[2];
// 状态列表 状态总数
LL state[2][mod],ans,val[15][15];
int HASH[mod];//每种状态的数目
LL get(int s,int p)//获取状态s从右向左数的第p位
{return (s>>((p-1)*2))&3;}
void change(int &s,int p,int v)//把状态s的第p位改成v
{
s^=get(s,p)<<((p-1)*2);
s^=(v)<<((p-1)*2);
}
void update(LL &x,LL y){x=max(x,y);}
void add(int now,int st,LL sum)
{
int ss=st%mod;
while(HASH[ss]!=-1&&list[now][HASH[ss]]!=st)
{
ss++;ss%=mod;
if(ss==0)ss=1;
}
if(HASH[ss]==-1)
{
HASH[ss]=++num[now];
list[now][num[now]]=st;
state[now][num[now]]=sum;
}
else update(state[now][HASH[ss]],sum);
}
void work()
{
state[0][1]=val[1][1];num[0]=1;list[0][1]=1;
//一开始的状态必须设成这样,确保起点为(1,1)
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
now^=1;
num[now]=0;
memset(HASH,-1,sizeof(HASH));
for(int k=1;k<=num[now^1];k++)
{
int st=list[now^1][k];
LL sum=state[now^1][k];
int p=get(st,j);
int q=get(st,j+1);
int d=get(st,j+2);//为了避免重复计算某个格子价值,引入d这个括号
if(!p&&!q)
{
add(now,st,sum);//因为不一定经过所有格子,所以有这种状态
if(map[i][j+1]&&map[i+1][j])
{
change(st,j,1);
change(st,j+1,2);
if(!d)add(now,st,sum+val[i][j]+val[i][j+1]+val[i+1][j]);
else add(now,st,sum+val[i][j]+val[i+1][j]);
//上面两个一定要加上val[i][j]啊!因为它的价值没有被累加过!
}
}
else if(!p&&q)
{
if(i==n&&j==m)
{
update(ans,sum);
continue;
}
if(map[i][j+1])//从上面到达(n,m)
{
if(!d)add(now,st,sum+val[i][j+1]);
else add(now,st,sum);
}
if(map[i+1][j])
{
change(st,j,q);
change(st,j+1,0);
add(now,st,sum+val[i+1][j]);
}
}
else if(p&&!q)
{
if(i==n&&j==m)//从左面到达(n,m)
{
update(ans,sum);
continue;
}
if(map[i+1][j])add(now,st,sum+val[i+1][j]);
if(map[i][j+1])
{
change(st,j,0);
change(st,j+1,p);
if(!d)add(now,st,sum+val[i][j+1]);
else add(now,st,sum);
}
}
else if(p==2&&q==1)
{
change(st,j,0);
change(st,j+1,0);
add(now,st,sum);
}
else if(p==1&&q==1)
{
int top=1;
for(int pos=j+2;pos<=m+1;pos++)
{
int temp=get(st,pos);
if(temp==1)top++;
if(temp==2)top--;
if(top==0)
{
change(st,j,0);
change(st,j+1,0);
change(st,pos,1);
add(now,st,sum);
break;
}
}
}
else if(p==2&&q==2)
{
int top=1;
for(int pos=j-1;pos;pos--)
{
int temp=get(st,pos);
if(temp==2)top++;
if(temp==1)top--;
if(top==0)
{
change(st,j,0);
change(st,j+1,0);
change(st,pos,2);
add(now,st,sum);
break;
}
}
}
}
}
for(int j=1;j<=num[now];j++)list[now][j]<<=2;
}
}
int main()
{
int Case=0;
while(scanf("%d%d",&n,&m)!=EOF)
{
ans=-(1LL<<31);now=0;
memset(map,0,sizeof(map));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%lld",&val[i][j]);
map[i][j]=1;
}
work();
printf("Case %d: %lld\n",++Case,ans);
}
}