小J真爱粉交流群

题目大意

有数字的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 行所有位置的路线,并使得max{fir[k]+f[i+1][k]}最小,其中 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 1表示下一个位置去 l l 还是r
那么总复杂度是三方。
详见代码。

#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);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值