原题:
取数游戏
描述
现在你的面前有一个 N*M 的矩阵,你需要进行恰好 K 次操作。每次 操作你可以选择其中一行或者其中一列,将其中的元素全部累加到 ans 里 去,然后把选中的这些数全部减去 P。问 ans 最大是多少。
输入
第一行一共四个数,分别为: N,M,K,P .
接下来给出一个 N 行 M 列的矩阵 Ai,j。
N,M ≤ 1000,K ≤ 100000 , 0 ≤ P ≤ 100 , 1 ≤ Ai,j ≤ 1000。
输出
一共一行,一个整数表示 ans_max .
输入样例 1
2 2 2 2
1 3
2 4
输出样例 1
11
思路题解:
标签 模拟
这道题很多人应该和我一样是模拟出来了的,我一开始的代码是每次用sort排序,然后取某行或某列和的最大值,再将该行或该列的值减去P,然后反复做,结果发现时间差不多在2s左右,题目要求是1s,问题来了就是如何优化。
数据结构——优先队列
在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。
默认优先级:从大到小排序。(故本题不需要改动,直接拿来用即可)
头文件在queue中
定义方式:priority_quue<数据类型>队列名;
q.push(x) 把x放入优先队列q中
q.pop(x) 把x弹出优先队列q中
q.top() 优先队列q队头(队列中优先级最高的元素)
q.empty() 判断队列是否为空,若为空返回true,返之flase
#include <queue>
priority_queue<ll>q;//定义名为q类型为ll的优先队列
//优先队列
for(int i=1;i<=m;i++) q.push(list[i]);//将各列的和放入优先队列中
for(int i=1;i<=k;i++)
{
ll tmp=q.top();q.pop();
y[i]=y[i-1]+tmp;
tmp-=1ll*n*p;
q.push(tmp);
}
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++) q.push(line[i]);
for(int i=1;i<=k;i++)
{
ll tmp=q.top();q.pop();
x[i]=x[i-1]+tmp;
tmp-=1ll*m*p;
q.push(tmp);
}
##核心:
打破数据行和列的区别
先对行和列在1到k的范围内分别求解,然后求出一个满足i和k-i的最大值,
注意1:结果会减去反复计算的,数据会爆int,这里用long long。
注意2:横向与纵向操作会有相交的部分,每相交一次就减去p ,总共减去 i∗(k−i)∗p 。
注意3:因为可能最终答案为一个很小的负数,ans初值要足够小,取-1e18。
AC代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
int n,m,k,p;
ll a[1005][1005];//虽然1 ≤ Ai,j ≤ 1000,但后面会减去足够多次的p,会爆int,所也用long long
ll line[1005],list[1005];
ll ans=-1e18;//ans足够小 防止答案为很小的负数(倘若不够小 则无法取max取到答案 而取ans的初值
ll x[1000005],y[1000005];
priority_queue<ll>q;
int main(){
scanf("%d%d%d%d",&n,&m,&k,&p);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%lld",&a[i][j]);
}
}
//求各行各列的和
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
line[i]+=a[i][j];
list[j]+=a[i][j];
}
}
for(int i=1;i<=m;i++) q.push(list[i]);
for(int i=1;i<=k;i++)
{
ll tmp=q.top();q.pop();
y[i]=y[i-1]+tmp;
tmp-=1ll*n*p;
q.push(tmp);
}
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++) q.push(line[i]);
for(int i=1;i<=k;i++)
{
ll tmp=q.top();q.pop();
x[i]=x[i-1]+tmp;
tmp-=1ll*m*p;
q.push(tmp);
}
for(int i=0;i<=k;i++){//核心部分打破行与列的区别 上面将行和列分开取 下面将取不同行数和列数的最大值取出
ans=max(ans,x[i]+y[k-i]-1ll*i*(k-i)*p);
}
cout<<ans<<endl;
return 0;
}