【XJOI】题解 邓哲也的矩阵-贪心

题目链接

 

时间:1s   空间:32M

题目描述:

有一个 n×m的矩阵,现在准备对矩阵进行k次操作,每次操作可以二选一

1:

选择一行,给这一行的每一个数减去p,这种操作会得到的快乐值等于操作之前这一行的和

2:

选择一列,给这一列的每一个数减去p,这种操作会得到的快乐值等于操作之前这一列的和

那么问题来了,k次操作最大可能得到的和是多少。

 

输入格式:

第一行输入4个整数n,m,k,p

接下来n行,每行输入m个整数a[i][j]

 

输出格式:

输出最大可能的和

 

样例输入1:

2 2 2 2
1 3
2 4

 

样例输出1:

11

 

样例输入2:

5 5 20 100 
464 757 53 708 262 
753 769 189 38 796 
394 60 381 384 935 
882 877 501 615 464 
433 798 504 301 301

 

样例输出2:

38013

 

样例输入3:

2 1 3 2
3 
3 

 

样例输出3:

8

 

约定:

1≤n,m≤1000,1≤k≤106,1≤p,a[i][j]≤1000

---------------------------------------------------------题解分割线--------------------------------------------------------------

看到是求最大和不可避免的考虑到贪心

那么来考虑一下贪心的正确性

首先考虑取行列的相互影响关系:可以发现当取x次行时,取了(k-x)次列

每取1次行,这一行的和会减少m*p,每一列的和会减少p

同样的每取1次列,这一列的和会减少n*p,每一行的和会减少p

然后考虑取行列的顺序关系:可以发现先取行和先取列对于答案的贡献是一样的

所以在取行列数量确定的情况下,取行列的顺序是没有影响的,所以考虑每次取大值得做法是正确的

那么只需要枚举取0到k次行,同时可以枚举出(k-0)次列的答案情况,然后ans取最大值即可

既然贪心是可行的,那么再考虑一下复杂度的问题,

如果每取一次就模拟的把每个数减去p的话,时间复杂度是O(n^2*m^2*k*log(n)*log(m)),肯定会T

所以我们考虑先把每行每列的和预处理出来一起处理,这样时间复杂度就是O(k*log(n)*log(m)),完全OK

(重要重要重要)PS. 数据十分毒瘤,inf一定要开的够大

-------------------------------------------------------代码分割线----------------------------------------------------------------

以下是AC代码:

#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;

typedef long long LL;
static const int N = 1010;
static const int M = 1e6 + 10;
static const LL inf = 99999999999999999;
LL n, m, p, k, ans = -inf;
LL mp, h[N], l[N], ansh[M], ansl[M];
priority_queue <LL> qh;
priority_queue <LL> ql;

int main ()
{
	scanf ("%lld%lld%lld%lld", &n, &m, &k, &p);
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
		{
			scanf ("%lld", &mp);
			h[i] += mp;
			l[j] += mp;
		}
	for (int i = 1; i <= n; ++i)
		qh.push(h[i]);
	for (int i = 1; i <= m; ++i)
		ql.push(l[i]);
	ansh[0] = ansl[0] = 0;
	for (int i = 1; i <= k; ++i)
	{
		LL x = qh.top();
		LL y = ql.top();
		qh.pop();
		ql.pop();
		ansh[i] = ansh[i-1] + x;
		ansl[i] = ansl[i-1] + y;
		qh.push(x-p*m);
		ql.push(y-p*n);
	}
	for (int i = 0; i <= k; ++i)
	{
		ans = max (ansh[i] + ansl[k-i] - (k-i) * i * p, ans);
	}
	printf ("%lld", ans);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值