矩阵游戏

矩阵游戏

题意

给出一个 \(n\)\(m\) 列的矩阵,进行恰好 \(k\) 次操作。

每次操作为选择一行或一列,分数为该行/列的和,操作结束后该行/列整体减去 \(p\)

求最大分数。

思路

先有一个错误的贪心,即每次选择行和列中最大的一个操作,但这样有下面的 hack:

1 4 4 10
9 9 9 9

如果第一次选择一行操作,以后都必须加负数了。

而一列一列的操作就不会加负数。

然后发现行和列独立,对一个行操作完后列的相对大小不变,对一个列操作完后行的相对大小不变。

所以可以把单独取行和单独取列的答案先算出来。

\(f_i\) 为行取 \(i\) 次的答案,\(g_i\) 为列取 \(i\) 次的答案。

可以用堆维护当前的行和,列和,取出最大后把 \(p\) 减掉。

然后枚举 \(i\) 表示行取 \(i\) 个列取 \(k-i\) 个,加起来去掉重复计算的点。

\[ans=\max_{i=0}^k f_i + g_{k-i}-i\times(k-i)\times p \]

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 1e3 + 5;
const int M = 1e6 + 5;

int n, m, k, p, a[N][N], f[M], g[M];
int h[M], l[M], ans = LLONG_MIN;
priority_queue <int> p1, p2;


signed main() {
	freopen("matrix.in", "r", stdin);
	freopen("matrix.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin >> n >> m >> k >> p;
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= m; j ++) {
			cin >> a[i][j];
			h[i] += a[i][j];
			l[j] += a[i][j];
		}
	}
	for (int i = 1; i <= n; i ++) p1.push(h[i]);
	for (int i = 1; i <= k; i ++) {
		f[i] = p1.top(); p1.pop();
		p1.push(f[i] - p * m);
		f[i] += f[i - 1];
	}
	for (int i = 1; i <= m; i ++) p2.push(l[i]);
	for (int i = 1; i <= k; i ++) {
		g[i] = p2.top(); p2.pop();
		p2.push(g[i] - p * n);
		g[i] += g[i - 1];
	}
	for (int i = 0; i <= k; i ++) 
		ans = max(ans, f[i] + g[k - i] - i * (k - i) * p);
	cout << ans << "\n";
	return 0;
}
posted @ 2024-11-04 21:19  maniubi  阅读(2)  评论(0编辑  收藏  举报