题目大意
有数字的n*m网格图,DDD和YJQ在玩游戏。
DDD先把一个小人放在第一行某个格子上。获得该格子分数。
接下来每个时刻,YJQ先在任意上下相邻的格子间建墙,可以建任意个。
DDD移动小人向四相邻的位置走(如果没有墙)。并获得新格子分数(重复走重复获得)。
小人到达最后一行游戏结束,现在DDD的目标是最小化,YJQ的目标是最大化,问得分是多少。
做法
可以发现,DDD负责决定在某一行的路线,YJQ会钦点它下哪个洞。
用
f[i][j]
f
[
i
]
[
j
]
表示从
(i,j)
(
i
,
j
)
这个位置开始到结束的分数。
那么DDD需要决策一条从
(i,j)
(
i
,
j
)
出发,只左右走,经过第
i
i
行所有位置的路线,并使得最小,其中
fir[k]
f
i
r
[
k
]
表示第一次到达
(i,k)
(
i
,
k
)
经过的距离。
我们可以做区间dp,
g[l,r,0/1]
g
[
l
,
r
,
0
/
1
]
表示路径一个后缀已经决定了,经过了
[1,l)
[
1
,
l
)
以及
(r,m]
(
r
,
m
]
,
0
0
和表示下一个位置去
l
l
还是。
那么总复杂度是三方。
详见代码。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=100+10;
int a[maxn][maxn],f[maxn][maxn],sum[maxn][maxn],g[maxn][maxn][2];
int i,j,k,l,r,mid,t,n,m,mi,ans,ca;
bool czy;
void work(int x){
int i,j,t;
fo(i,1,m)
fo(j,i,m) g[i][j][0]=g[i][j][1]=1000000020;
g[1][m][0]=g[1][m][1]=0;
fo(i,1,m)
fd(j,m,i+1){
t=max(g[i][j][0],f[x+1][i])+sum[i][i];
g[i+1][j][0]=min(g[i+1][j][0],t);
t=max(g[i][j][0],f[x+1][i])+sum[i][j-1];
g[i+1][j][1]=min(g[i+1][j][1],t);
t=max(g[i][j][1],f[x+1][j])+sum[j][j];
g[i][j-1][1]=min(g[i][j-1][1],t);
t=max(g[i][j][1],f[x+1][j])+sum[i+1][j];
g[i][j-1][0]=min(g[i][j-1][0],t);
}
fo(i,1,m)
f[x][i]=max(min(g[i][i][0],g[i][i][1]),f[x+1][i])+sum[i][i];
}
int main(){
freopen("fan.in","r",stdin);freopen("fan.out","w",stdout);
scanf("%d",&ca);
while (ca--){
scanf("%d%d",&n,&m);
//if (n<=2) continue;
czy=1;
fo(i,1,n)
fo(j,1,m){
scanf("%d",&a[i][j]);
if (a[i][j]!=1) czy=0;
}
//if (czy&&n>2) continue;
if (n==1){
ans=a[1][1];
fo(j,2,m) ans=min(ans,a[1][j]);
printf("%d\n",ans);
continue;
}
fo(i,1,m) f[n][i]=a[n][i];
fd(i,n-1,1){
fo(j,1,m){
sum[j][j]=a[i][j];
fo(k,j+1,m) sum[j][k]=sum[j][k-1]+a[i][k];
}
work(i);
}
ans=f[1][1];
fo(j,2,m) ans=min(ans,f[1][j]);
printf("%d\n",ans);
}
}