ZOJ Problem Set - 3941 Kpop Music Party

题目链接

(贪心+暴力枚举)

题意:给出t组测试样例,每组会先给出一行N,K,M(表示N个2活动时间段,每参加一次活动可以开心持续K天,最多能参加M次活动)随后是N行,每行2个数a[i].l,a[i].r表示每段活动的起止时间【l ,r】,问你他最多能开心多少天

分析:首先对于每个点最大能获得K的贡献(不受其他段影响时),对于R+1-L>>K时我们取第1个点,第k+1,...,第k+cnt个点,那么是最优的情况。对于一段区间取能完全覆盖住整段区间的cnt数量,这时候若是m还有剩,那么我们可以取最后一个点,可能还能再贡献对一些,那么对于有多段的时候我们就需要考虑2点,第一:你前一段取完全覆盖后有可能蔓延到了下一段区间内,那么我们就需要定一个p来记录前一段最终的位置;第二:考虑每段区间最后一个点取不取的情况,因为N<10,因此我们可以dfs暴力枚举出每段区间取不取最后一个点的情况。(若是没看懂可以考虑从代码下手,代码注释丰富)

解法:首先我们需要先对拿到的可能重合的数据段进行合并操作,然后通过dfs暴力枚举出所有段的情况取最大ans即可。

          //注意这里的合并操作是【1,4】【5,6】=》【1,6】

代码如下

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
#define ll long long
const int maxn = 1e5 + 500;
struct node {
	int l, r;
}a[15];
int top; 
int k, m, ans;
bool cmp(node x, node y) {
	if (x.l != y.l)
		return x.l < y.l;
	return x.r < y.r;
}
//i表示第i段,p是指前一项取完后的位置,m表示剩余的选取次数,sum是当前的值
void dfs(int i, int p, int m, int sum) {
	if (m == 0 || i > top) {
		ans = max(ans, sum);
		return;
	}
	p = max(p, a[i].l);
	int cnt = (a[i].r + 1 - p + (k - 1)) / k;
	//cnt分母加k-1有2个作用,一个是保证这段区间能取就取,尽量超过,另一个是对于p>a[i].r的情况,保证cnt=0
	if (m <= cnt) { ans = max(ans, sum + m*k); return; }//m不够直接比较然后结束
	m -= cnt;
	p += cnt*k;
	sum += cnt*k;
	dfs(i + 1, p, m, sum);//这里最后一个点也可能取过了
	int nxt = a[i].r + k;
	m--;
	sum += nxt - p;//这里的nxt-p>=0(因为可能取过)
	p = nxt;
	dfs(i + 1, p, m, sum);
	//这里最后一个点可能取第二次了(只可能减小当前sum的数据,不影响最终ans,因为这时候他不是最优秀的答案)
}
int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		int n;
		scanf("%d%d%d", &n, &k, &m);
		for (int i = 0; i < n; i++)
			scanf("%d%d", &a[i].l, &a[i].r);
		top = 0;
		ans = 0;
		sort(a, a + n, cmp);
		for (int i = 1; i < n; i++) {//对区间进行合并处理
			if (a[top].r + 1 >= a[i].l) 
				a[top].r = max(a[top].r, a[i].r);
			else {
				a[++top].l = a[i].l;
				a[top].r = a[i].r;
			}
		}
		dfs(0, 0, m, 0);
		printf("%d\n", ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41156591/article/details/80101887