入门OJ:简单的网络游戏

题目描述

在某款极具技术含量的网络游戏中,佳佳靠着他的聪明智慧垄断了游戏中的油田系统。油田里有许多油井,这些油井排成一个M*N的矩形。每个油井都有一个固定的采油量。每两个相邻的油井之间有一条公路,这些公路是油井与油井之间唯一的运油方式。佳佳的领地在油田的右方和下方,他需要把采到的油通过这些公路运输到他的领地。为了保证采到的油以最快的方式供给右方和下方的领地,对于每个油井,佳佳总是将采到的油分成非空的两部分将其中一部分沿公路一直向右运输到油田的右边界,将另一部分沿公路一直向下运输到油田的下边界。然而树大招风,网络游戏中的GM以保证游戏公平为由,要求佳佳主动贡献出K个油井。更惨的是,失去某些油井不单意味着采油量减少,这还将会导致运输线路的中断。如果佳佳贡献出了某个油井,那么所有要经过这个油井的运输任务将无法完成。佳佳想保证贡献K个油井之后自己的损失最小(损失即为失去的所有油井的采油量之和),而他希望其他的所有油井还能够像往常一样正常运输。于是他向你求救,希望你能帮助他实现他的想法。

输入格式

第一行有三个用空格隔开的正整数M,N和K,它们分别表示油田的长、宽和佳佳需要贡献出的油田数。以下N行中每行有M个用空格隔开的正整数。这些正整数保证不超过10000,它们描述了油田中各个油井的采油量。 输入数据保证K<=M*N,M<=60,N<=60。

输出格式

一个整数,表示佳佳最少要损失的采油量。


分析题目后可以得出这样一个结论:

设c(i,j)表示第i行前j个油井的采油量之和,那么放弃点(x,y)的油井的代价就是:c(x,y)。由于c数组当i确定的时候随j的变大单调不下降,所以我们对于第i行放弃的油井应该为从左端点开始的连续一段油井。同理对于第i列放弃的油井也应该为从上断电开始的连续一段油井。

通过这个结论可以进一步推导:

当前行选择的油井数必须小于等于上一行选择的油井数。
于是我们很容易设计出这样的状态:dp(i,j,k)表示第i行选了j个油井,并且1~i行一共选了k个油井(不是题目的K)。那么我们可以列出转移方程:

\[ dp[i][j][k]=Min{\{}dp[i-1][l][j-k]+c[i][k]{\}} \]

其中l为上一行选择的油井数。初始状态dp(1,i,i)=c(1,i),目标状态为Min{dp(n,i,K)}。

* 一个明显的优化是dp数组的第一维可以滚掉。

#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 61
#define maxm 3601
using namespace std;
 
int dp[maxn][maxn][maxm],c[maxn][maxn];
int n,m,sum;
 
inline int read(){
    register int x(0),f(1); register char c(getchar());
    while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
    while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
 
int main(){
    m=read(),n=read(),sum=read();
    for(register int i=1;i<=n;i++){
        for(register int j=1;j<=m;j++) c[i][j]=c[i][j-1]+read();
    }
 
    memset(dp,0x3f,sizeof dp);
    for(register int i=0;i<=m;i++) dp[1][i][i]=c[1][i];
    for(register int i=2;i<=n;i++){
        for(register int j=0;j<=min(sum,i*m);j++){
            for(register int k=0;k<=min(sum,min(m,j/i));k++){
                for(register int l=k;l<=min(sum,min(m,(j-k)/(i-1)));l++){
                    if(j>l) dp[i][k][j]=min(dp[i][k][j],dp[i-1][l][j-k]+c[i][k]);
                }
            }
        }
    }
    int ans=0x3f3f3f3f;
    for(register int i=0;i<=min(sum,m);i++){
        ans=min(ans,dp[n][i][sum]);
    }
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/akura/p/11045358.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值