题意:给出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;
}