codeforces 446B DZY Loves Modification
题目描述
游戏的具体内容是这样的,有一个坑坑洼洼的地面,也就是一个n*m的矩阵。对于每次操作,他可以选择任意一行或者是任意一列。从选择这一行或者是这一列上过去时,便可以使这一行或者是这一列的高度下降p,同时熊本熊获得的分数为这一行或者是这一列高度减小之前的高度和,每个位置上的高度可以为负数。(也就是说分数有可能会减少)为了得分更高,DZY想知道在这个矩阵上做k此操作能够达到的最大分数。
输入输出格式
输入格式:
第1行:四个整数N,M,K,P(1 ≤ n, m ≤ 10^3;1 ≤ k ≤ 10^6;1 ≤ p ≤ 100)
第2~N+1行:每行M个整数表示矩阵(1<=aij<=10^3)
输出格式:
共1行:最大的分数
样例数据
input
2 2 2 2
1 3
2 4
output
11
input
2 2 5 2
1 3
2 4
output
11
数据范围
对于10%的数据 1 ≤ n,m ≤ 10
对于30%的数据 1 ≤ n,m ≤ 500
对于100%的数据1 ≤ n, m ≤ 10^3;1 ≤ k ≤ 10^6;1 ≤ p ≤ 100
很容易想到贪心,维护两个堆,每次取最大的行和列,再减去n*p(或m*p),加入堆中
但是这样是错的,反例就是当有一行和一列相同时,选一行或一列都可能是最优解
首先我们设最终选了 行 i 次,则列选了 k-i 次
那么假设我们先全部选行,然后选列,则每次选列时,要-= i*p
这样最后是 -= i*(k-i)*p
也就是所有行对列的影响
那我们先把这个 i*(k-i)*p 提出来,那么选行和选列就互不影响
就可以分别考虑行和列
对于只取行的情况:
预处理出选0次 1次······k次行的最大值 H[i]
即优先队列跑一次即可
同理对列处理, 得到 L[i] 表示取i次列, 0次行的最大值
然后 ans = max( H[i]+L[k-i] - i*(k-i)*p )
注意结果可能是很小的负数,ans = -inf ,inf要足够大
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<queue> 6 using namespace std; 7 priority_queue<long long>Q; 8 long long n,m,k,p,a[1001][1001],b[1001][1001],A[1000001],B[1000001],ans=-2e16; 9 long long max(long long x,long long y) 10 { 11 if (x<y) return y; 12 else return x; 13 } 14 int main() 15 {long long i,j; 16 //freopen("game.in","r",stdin); 17 //freopen("game.out","w",stdout); 18 cin>>n>>m>>k>>p; 19 for (i=1;i<=n;i++) 20 { 21 for (j=1;j<=m;j++) 22 { 23 scanf("%I64d",&a[i][j]); 24 b[i][j]=a[i][j]; 25 a[i][j]+=a[i][j-1]; 26 b[i][j]+=b[i-1][j]; 27 } 28 } 29 for (i=1;i<=n;i++) 30 Q.push(a[i][m]); 31 for (i=1;i<=k;i++) 32 { 33 long long x=Q.top(); 34 Q.pop(); 35 A[i]=A[i-1]+x; 36 Q.push(x-m*p); 37 } 38 while (Q.empty()==0) Q.pop(); 39 for (i=1;i<=m;i++) 40 Q.push(b[n][i]); 41 for (i=1;i<=k;i++) 42 { 43 long long x=Q.top(); 44 Q.pop(); 45 B[i]=B[i-1]+x; 46 Q.push(x-n*p); 47 } 48 for (i=0;i<=k;i++) 49 { 50 ans=max(ans,A[i]+B[k-i]-i*(k-i)*p); 51 } 52 cout<<ans; 53 }