玩游戏 堆+预处理

题目背景

众所周知,熊本熊的博客是kakakakakaka。有一天他浏览长郡一位大佬的博客,在其中他发现了一个游戏,他玩得饶有兴致,但是他想知道怎么拿到最高分,于是他想让你来帮帮他。

题目描述

游戏的具体内容是这样的,有一个坑坑洼洼的地面,也就是一个n*m的矩阵,熊本熊必须开着他的推土车完成整个游戏。对于每次操作,他可以选择任意一行或者是任意一列。当熊本熊的推土机从这一行或者是这一列上开过去时,便可以使这一行或者是这一列的高度下降p,同时熊本熊获得的分数为这一行或者是这一列高度减小之前的高度和,每个位置上的高度可以为负数。(也就是说熊本熊的分数有可能会减少)为了得分更高,熊本熊想知道在这个矩阵上做k此操作能够达到的最大分数。

输入输出格式

输入格式:

1行:四个整数N,M,K,P(1 ≤ n, m ≤ 10^3;1 ≤ k ≤ 10^6;1 ≤ p ≤ 100)

2N+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


来自熊本熊的考试题目,一开始我们大部分人都是想到一种贪心的方法:

我们经过一定的分析可以知道,每行相对的和是修改每列时候是不变的,同时每列的关系在修改每行的时候也是不变的。

那么我们就可以建立两个大根堆,一个存每行之和,一个存每列之和,同时我们用两个数x,y记录选了几行几列。

然后我们进行k次选择,每次我们判断存行的堆顶减去p乘以选了几列和存列的堆顶减去p乘以选了几行,然后记录。

但是,这种贪心方法是不对的。

于是我们不能用这种贪心的方法,但是我们仍然可以确定,他们地相对大小是不变的,于是我们有了第二种方法,即正解。

因为相对关系是不变的,所以我们可以先预处理出选1,2,...k行的最大值,选1,2,...k列的最大值。

然后我们最后在for一遍,从0到k,因为我们知道,最后的分数是行【i】+列【k-i】-(i)*(k-i)*p,从而就可以得到最大分数。

但是我发现做堆的题目时,一定要仔细检查堆是否写对,总是写错。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<algorithm>
#define ll long long
#define il inline
#define db double
using namespace std;
priority_queue<long long>Q;
il ll gl()
{
    ll x=0,y=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        y=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*y;
}
ll map[1045][1045];
ll x[1000045],y[1000045];
int main()
{
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    ll n=gl(),m=gl(),k=gl(),p=gl();
    for(ll i=1;i<=n;i++)
    {
        ll sum=0;
        for(ll j=1;j<=m;j++)
        {
            map[i][j]=gl();
            sum+=map[i][j];
        }
        Q.push(sum);
    }
    for(ll i=1;i<=k;i++)
    {
        ll h=Q.top();
        Q.pop();
        x[i]=x[i-1]+h;
        Q.push(h-m*p);
    }
    while(!Q.empty())
    Q.pop();
    for(ll i=1;i<=m;i++)
    {
        ll sum=0;
        for(ll j=1;j<=n;j++)
        sum+=map[j][i];
        Q.push(sum);
    }
    for(ll i=1;i<=k;i++)
    {
        ll h=Q.top();
        Q.pop();
        y[i]=y[i-1]+h;
        Q.push(h-n*p);
    }
    ll ans=-2e16;
    for(ll i=0;i<=k;i++)
    {
        ll now=x[i]+y[k-i]-i*(k-i)*p;
        if(now>ans)
        ans=now;
    }
    printf("%lld\n",ans);
    return 0;
}

 

 

玩游戏

posted @ 2017-08-19 15:57  GSHDYJZ  阅读(176)  评论(0编辑  收藏  举报