【题解】牛客多校六 J Upgrading Technology⭐⭐⭐ 【前缀和】

牛客多校六 J Upgrading Technology

Rowlet is playing a very popular game in the pokemon world. Recently, he has encountered a problem and wants to ask for your help.

In this game, there is a technology tree system. There are n kinds of technology in this game, each of them has m levels numbered from 1 to m. In the beginning, all technologies have no level (regard as level 0). When the i-th technology is at the (j - 1)-th level, the player can pay c_{i j}c
ij

pokedollars (currency used in this game) to upgrade this technology into the j-th level. However, sometimes upgrading is so easy that the cost might be negative, which implies the player may gain profit from upgrading technologies.

Moreover, if all technologies have been upgraded to level j, the player will gain an additional profit of d_{j}d
j

pokedollars. However, sometimes too many technologies of the same level might be confusing, hence the profit can be negative as well.

Rowlet wants to determine the optimal strategy that can bring him the most pokedollars. Help him to find the maximum gain. Note that Rowlet may upgrade nothing, and in that case, the profit is zero.

Input

链接:https://ac.nowcoder.com/acm/contest/886/J
来源:牛客网

There are multiple test cases. The first line contains an integer T (1 \leq T \leq 101≤T≤10), indicating the number of test cases. Test cases are given in the following.

For each test case, the first line contains two integers n, m (1 \leq n, m \leq 10001≤n,m≤1000), representing the number of technologies and the number of levels respectively.

The i-th of the next n lines contains m integers, where the j-th number is c_{i j}c
ij (-10^{9} \leq c_{i j} \leq 10^{9}−10 9≤c ij​ ≤10 9 ).The last line contains m integers, where the j-th number is d_{j}d j​ (-10^{9} \leq d_{j} \leq 10^{9}−10 9 ≤d j​ ≤10 9 ).We ensure that the sum of n \cdot mn⋅m in all test cases is at most 2 \times 10^{6}2×10 6 .

Output

For each test case, output “Case #x: y” in one line (without quotes), where x indicates the case number starting from 1, and y denotes the answer(in pokedollars) to this test case.

Examples

输入
复制
2
2 2
1 2
2 -1
4 1
3 3
1 2 3
1 2 3
1 2 3
6 7 8
输出
复制
Case #1: 2
Case #2: 4

Hint




题意:

游戏里面有n个科技树, 每个科技树均有m层科技, 只能从下往上连续的点技能(n*m矩阵), 每个点有一个权值(有正有负), 另有d[i]表示点满了第i层所有科技可以减去的权值, 求最小权值

题解:

很显然这道题是一道前缀和的题目
我们定义受益为正数, 代价为负数, a[i][j]表示第 i 条升级到 j 层时的代价和, d[i] 表示连续取前 i 层的额外受益和

显然有两种取法, 一种是一次取满一层拿到额外受益, 一种是把某一条单独取得很高, 且两者冲突, 即不能把所有条都向上取.

我们遍历枚举取满前 j 层, 然后再枚举每一层向上取到当前最优结果, 如果所有条都取了产生冲突, 我们就取条一条最小的.

这里就到了本题的关键, 不断确定每条向上取的最优结果, 这里有多种实现方式, 如线段树, ST表都可以, 这里我们选取最优的一种, 即构建一个Max[i][j] 表示第 i 条已经取到 j 层时向上取的最优权值.
也就是最优前缀和数组, 从后向前遍历即可求得

经验小结:

仔细分析, 想清楚再写


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef  long long LL;
const LL inf = 0x3f3f3f3f3f3f3f3f;
const LL maxn = 1010;

LL n, m, d[maxn], a[maxn][maxn], Max[maxn][maxn];
int main()
{
    int T;
    cin >> T;
    for(int t = 1; t <= T; ++t){
        fill(Max[0], Max[0]+maxn*maxn, -inf);
        cin >> n >> m;
        for(int i = 1; i <= n; ++i){
            for(int j = 1; j <= m; ++j){
                cin >> a[i][j];
                a[i][j] = -a[i][j]+a[i][j-1]; //代价为负数, 受益为正数
            }
            Max[i][m] = a[i][m];
            for(int j = m-1; j >= 0; --j)
                Max[i][j] = max(Max[i][j+1], a[i][j]); //后缀求s前缀和的最大前缀和
        }
        for(int i = 1; i <= m; ++i){
            cin >> d[i];
            d[i] += d[i-1];
        }
        LL ans = 0, cur = 0, cnt = 0, mi;
        for(int j = 0; j <= m; ++j){
            //第j层以内全取
            cur = d[j], cnt = 0, mi = inf;
            for(int i = 1; i <= n; ++i){
                cur += a[i][j];             //先加上一定选的
                if(Max[i][j]-a[i][j] > 0){  //如果可往后则选择
                    cur += Max[i][j]-a[i][j];
                    ++cnt;
                    mi = min(mi, Max[i][j]-a[i][j]);
                }
            }
            if(cnt == n) //取了n条, 则必须要退回一条
                cur -= mi;
            ans = max(ans, cur);
        }
        cout << "Case #" << t <<": " << ans << endl;
    }

	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值