【DP】简单的网络游戏

Description

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

Input

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

Output

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

Sample Input

5 3 4
3 4 1 4 5
1 10 7 8 13
3 5 8 9 11

Sample Output

9
佳佳贡献出下面标有“x”的油井是符合条件的最小损失方案。
x x x 4 5
x 10 7 8 13
3 5 8 9 11 

分析

根据题目要求我们可以发现,被舍弃的油井一定是从左上角向右下角递减,或者说向左上角聚集。

我们可以把状态设计成三维(i,j,k)表示第i行舍弃了j个油井,目前为止共舍弃了k个油井。

然后我们枚举上一行舍弃的数量l,到上一行为止总共舍弃的数量就是k - j

那么就有状态转移方程

dp[i][j][k] = min(dp[i][j][k], dp[i - 1][l][k - j] + w[i][j]);

注意上界优化。

代码

#include<bits/stdc++.h>
using namespace std;
 
int n, m, kk;
int w[67][67];
int dp[67][67][3617];
int ans = INT_MAX;
 
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
    return x * f;
}
 
int main() {
    m = read(), n = read(), kk = read();
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            w[i][j] = w[i][j - 1] + read();
        }
    }//前缀和
    memset(dp, 0x3f3f3f3f, sizeof dp);
    for (int i = 1; i <= n; i++) {
        dp[i][0][0] = dp[0][i][0] = dp[0][0][i] = 0;
    }//初始化
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= min(m, kk); j++) {//上界优化,当前行最多舍弃数量
            for (int k = j; k <= min(m * i, kk); k++) {//上界优化,到目前为止最多舍弃数量
                for (int l = j; l <= min(m, kk); l++) {//上界优化,同j循环
                    dp[i][j][k] = min(dp[i][j][k], dp[i - 1][l][k - j] + w[i][j]);
                }
            }
        }
    }
    for (int i = 0; i <= min(m, kk); i++) {
        ans = min(ans, dp[n][i][kk]);
    }
    printf("%d", ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值