【贪心】(雾) 小Y的炮

Description

小Y最近开发出了批量制造大威力轰山炮的方法。才过去不到几个月,小Y就制造出了M门款
式不同的轰山炮。第i门轰山炮发射一次,能使一座当前高度不高于Ai的山的高度降低Di(
当然山的高度不能轰到0以下)。应政府要求,小Y要用他开发的轰山炮轰平开发区的几座山
。由于开发区急需土地资源,政府要求小Y轰平尽量多的山(轰平:使山的高度降低至0)。
但是小Y制造的弹药有限,导致他最多只能发射K次。
小Y想知道,他最多能轰平几座山?轰平这些山后,弹药最多还够他发射几次?

Input

第一行三个正整数N,M,K,分别表示山的数目、轰山炮的款式数目、最多发射次数。
接下来N行,每行一个正整数Hi,表示第i座山的高度,输入数据保证Hi是降序的(从大到小
)。
接下来M行,每行两个正整数Ai,Di,分别表示第i款轰山炮能轰的山的最高高度,和轰掉的
山高度的减少值。
N<=250000,M<=500,K,Hi,Ai<=10^18,Di<=500

Output

一行两个整数Max,Remain,分别表示最多轰平的山的数目和轰平这些山后最多的剩余发射次
数。

Sample Input

3 2 3
8
6
2
10 6
6 5

Sample Output

2 1

分析

附上标准题解虽然我并不知道这兄弟为什么会有。

简单来说这道题的思路就是先预处理出每座山被轰平的最少次数,然后尽可能地多轰平几座山,也就是从次数少的开始枚举。

重点就在于如何预处理出被轰平的最少次数。

首先,因为要使次数尽可能少,所以我们应该在所有能轰到当前山的炮中选择威力更大的,于是乎我们很愉快地先进行排序。

在这里有一个小优化,如果对于某个炮存在另外一个炮轰得比这个炮高,威力比这个炮大,那么这个炮就没有什么用,可以直接舍弃。

然后,我们可以采用递推的方法

因为是升序排序,所以新枚举到的这个炮一定比上一个炮轰的高度要高,我们可以算出用当前这个炮要把山轰到上一个炮的极限高度以内需要几发炮弹( t ),然后直接递推即可

f[H] = f[H - t * Di] + t;

代码

#include<bits/stdc++.h>
using namespace std;
struct node {
    long long a, d;
} e[510], p[510];
long long cnt;

long long n, m, k;
long long h[250010];
long long ans;

map<long long, long long> f;

inline long long read() {
    long long x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
    return x * f;
}

bool cmp(node a, node b) {
    if (a.a == b.a) return a.d < b.d;
    return a.a < b.a;
}//升序排序 

int main() {
//  freopen("cannon20.in", "r", stdin);
    n = read(), m = read(), k = read();
    for (long long i = n; i >= 1; i--) h[i] = read();
    for (long long i = 1; i <= m; i++) e[i].a = read(), e[i].d = read();
    sort(e + 1, e + m + 1, cmp);
    for (long long i = 1; i <= m; i++) {
	while (cnt && e[i].d >= p[cnt].d) cnt--;
	p[++cnt] = e[i];
    }//去掉没用的炮 
    long long tmp = 0;
    f[0] = 0;
    for (long long i = 1; i <= cnt; i++) {
	tmp = p[i - 1].a;
	long long l = max(tmp, p[i].a - p[i].d);
	for (long long j = l + 1; j <= p[i].a; j++) {
	    long long t = (j - tmp - 1) / p[i].d + 1;
	    f[j] = f[max(0 * 1ll, j - t * p[i].d)] + t;
	}//预处理 
    }
    long long kk = 1;
    tmp = 0;
    for (long long i = 1; i <= n; i++) {
	while (kk <= cnt && h[i] > p[kk].a) kk++;
	if (kk > cnt) {
	    printf("%lld %lld\n", i - 1, k - ans);
	    return 0;
	}//特殊情况 炮能够轰完所有的山 且炮弹足够 
	tmp = p[kk - 1].a;
	long long t = (h[i] - tmp - 1) / p[kk].d + 1;
	long long r = f[max(0 * 1ll, h[i] - t * p[kk].d)] + t;
	if (ans + r > k) {//炮弹不够了 
	    printf("%lld %lld\n", i - 1, k - ans);
	    return 0;//退出 
	}
	ans += r;
    }
    printf("%lld %lld\n", n, k - ans);
}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值