小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
    评论
很高兴为您解答这个问题!根据您的要求,我为您提供以下C语言程序: #include <stdio.h> #include <stdbool.h> int main() { int n; scanf("%d", &n); // 输入舞台矩阵的大小 int a[n][n]; for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { scanf("%d", &a[i][j]); // 输入观众数量 } } int row_max = -1, col_max = -1, cross_max = -1; // 初始化最大值 int row_idx = -1, col_idx = -1, cross_i = -1, cross_j = -1; // 初始化最大值的下标 // 计算每行和每列的观众数量总和 int row_sum[n], col_sum[n]; for(int i=0;i<n;i++) { row_sum[i] = 0; col_sum[i] = 0; for(int j=0;j<n;j++) { row_sum[i] += a[i][j]; col_sum[i] += a[j][i]; } } // 计算十字手势的最大值 for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { int sum = row_sum[i] + col_sum[j] - a[i][j]; // 计算某个点的观众总数 if(sum > row_max) { row_max = sum; row_idx = i; col_idx = j; } } } // 计算X手势的最大值 bool is_odd = n%2==1; // 判断舞台矩阵大小是奇数还是偶数 if(is_odd) { cross_i = n/2; // 中心点行坐标 cross_j = n/2; // 中心点列坐标 cross_max = a[cross_i][cross_j]; // 中心点观众数量 for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { if(i==cross_i && j==cross_j) // 如果是中心点就跳过 { continue; } if(abs(i-cross_i) == abs(j-cross_j)) // 符合X手势的点 { int sum = a[i][j]; if(sum > cross_max) { cross_max = sum; row_idx = i; col_idx = j; } } } } } else { cross_max = -1; } // 根据最大值的类型输出结果 if(row_max >= col_max && row_max >= cross_max) { printf("%d %d 十字手势\n", row_idx, col_idx); } else if(col_max >= row_max && col_max >= cross_max) { printf("%d %d 十字手势\n", row_idx, col_idx); } else if(cross_max >= row_max && cross_max >= col_max) { printf("%d %d X手势\n", row_idx, col_idx); } return 0; } 希望我的回答对您有帮助!如果您还有其他问题,请随时提出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值