(贪心+暴力枚举)
题意:给出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; }