Codeforces Gym101341K:Competitions(DP)

http://codeforces.com/gym/101341/problem/K

题意:给出n个区间,每个区间有一个l, r, w,代表区间左端点右端点和区间的权值,现在可以选取一些区间,要求选择的区间不相交,问最大的权和可以是多少,如果权和相同,则选区间长度最短的。要要求输出区间个数和选了哪些区间。

思路:把区间按照右端点排序后,就可以维护从左往右,到p[i].r这个点的时候,已经选择的最大权和是多少,最小长度是多少,区间个数是多少。

因为可以二分找右端点小于等于当前区间的左端点的某个区间(index),然后就有

dp[i] = max(dp[index] + w[index], dp[i]).

这题的我觉得困难的是打印使用了哪些区间,一开始想的方法超时了。

因为用pre数组记录路径,但是路径只是代表当前的解从哪里更新过来的,而不能记录是由哪一个点推出新的最优解。

一开始我用了一个bool型的vis数组判断是否在第i个点更新的答案,然后每次往前找,这样最坏会达到O(n^2)。

但是这样肯定扫了很多重复的情况,因此还可以用递推来构造vis数组,代表当前这个解最后使用了哪一个区间推出来。pre数组也使用递推的形式。

每次如果更新的了,就vis[i] = i,pre[i] = index,否则就vis[i] = vis[i-1], pre[i] = pre[i-1]。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 #define N 200010
 5 #define INF 0x3f3f3f3f
 6 struct node {
 7     int l, r, w, id;
 8     bool operator < (const node &rhs) const {
 9         if(r != rhs.r) return r < rhs.r;
10         return l < rhs.l;
11     }
12 } p[N];
13 LL dp[N], len[N];
14 int tol[N], pre[N], vis[N];
15 vector<int> ans;
16 // vis表示新的DP答案由哪个推出来的
17 // pre表示当前这个点可以由哪一个点跳过来
18 // dp[i]表示到第i个右端点的时候最大权和
19 // len[i]表示到第i个右端点的时候最小长度
20 // tol[i]表示区间个数
21
22 int main() {
23     int n; scanf("%d", &n);
24     for(int i = 1; i <= n; i++) scanf("%d%d%d", &p[i].l, &p[i].r, &p[i].w), p[i].id = i;
25     sort(p + 1, p + 1 + n);
26     for(int i = 1; i <= n; i++) {
27         dp[i] = dp[i-1], len[i] = len[i-1], tol[i] = tol[i-1], pre[i] = pre[i-1], vis[i] = vis[i-1];
28         node now = (node) { INF, p[i].l, 0, 0 };
29         int index = upper_bound(p + 1, p + n + 1, now) - p - 1;
30 //        while(!vis[index] && index) index--;
31         if(dp[index] + p[i].w > dp[i]) {
32             dp[i] = dp[index] + p[i].w;
33             len[i] = len[index] + p[i].r - p[i].l;
34             tol[i] = tol[index] + 1;
35             pre[i] = index; vis[i] = i;
36         } else if(dp[index] + p[i].w == dp[i] && len[index] + p[i].r - p[i].l < len[i]) {
37             len[i] = len[index] + p[i].r - p[i].l;
38             tol[i] = tol[index] + 1;
39             pre[i] = index; vis[i] = i;
40         }
41 //        printf("index : %d %d %d %d %lld\n", p[i].id, p[vis[i]].id, p[index].id, p[pre[i]].id, dp[i]);
42     }
43     int ed = n;
44     while(ed) { ans.push_back(p[vis[ed]].id); ed = pre[ed]; }
45     sort(ans.begin(), ans.end());
46     printf("%d %lld %lld\n", tol[n], dp[n], len[n]);
47     for(int i = 0; i < ans.size(); i++) printf("%d%c", ans[i], i + 1 == ans.size() ? ‘\n‘ : ‘ ‘);
48     return 0;
49 }
时间: 2024-10-07 18:33:54

Codeforces Gym101341K:Competitions(DP)的相关文章

HDU 1260:Tickets(DP)

Tickets Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 923    Accepted Submission(s): 467 Problem Description Jesus, what a great movie! Thousands of people are rushing to the cinema. However,

POJ 2192 :Zipper(DP)

恢复 DP一直以来都没怎么看,觉得很难,也只会抄抄模板和处理一些简单的背包.于是比赛出现了这道比较简单的题还是不会,要开始去学学DP了. 题意:给出3个字符串,分别为A串和B串还有C串,题目保证A的长度+B的长度=C的长度,C里面包含着A和B的字符,然后判断A和B在C里面的字符顺序还是不是和原来的A和B保持原样. 查看题目 做法:建立一个DP数组dp[i][j],第一维i表示使用了A的前i个字符,第二维j表示使用了B的前j个字符,然后赋予1或者0判断是否可以组成.如果此时c[i+j]==a[i]

Codeforces 1051 D.Bicolorings(DP)

Codeforces 1051 D.Bicolorings 题意:一个2×n的方格纸,用黑白给格子涂色,要求分出k个连通块,求方案数. 思路:用0,1表示黑白,则第i列可以涂00,01,10,11,(可以分别用0,1,2,3表示),于是定义dp[i][j][k]:涂到第i列分为j个连通块且第i列涂法为k的方案数,则有了代码中的转移式,共16种转移类型. #include<iostream> #include<cstdio> #include<algorithm> #in

Codeforces 543C Remembering Strings(DP)

题意比较麻烦 见题目链接 Solution: 非常值得注意的一点是题目给出的范围只有20,而众所周知字母表里有26个字母.于是显然对一个字母进行变换后是不影响到其它字符串的. 20的范围恰好又是常见状压DP的范围,所有状态压缩后用DP[sta]代表对应位的字符串已经满足要求的最小花费. 转移的时候,对一个字符串,逐列判断使它满足条件的最小花费,记录使用这个策略对sta的影响. 即对同一列有两种情况,直接变换该字符串的这一位,或者变换这一列的sum-1个有相同字符的位置(去掉代价最大的). #in

HDU 5791:Two(DP)

http://acm.hdu.edu.cn/showproblem.php?pid=5791 Two Problem Description Alice gets two sequences A and B. A easy problem comes. How many pair of sequence A' and sequence B' are same. For example, {1,2} and {1,2} are same. {1,2,4} and {1,4,2} are not s

POJ 1260:Pearls(DP)

http://poj.org/problem?id=1260 Pearls Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 8474   Accepted: 4236 Description In Pearlania everybody is fond of pearls. One company, called The Royal Pearl, produces a lot of jewelry with pearls

codeforces 489F Special Matrices(DP)

传送门:点击打开链接 题目大意: 给定一个n*n的01矩阵的前m行,要求求出有多少种构造方案使得:每一行,每一列的1的个数都是2 解题思路: 既然已经给定了前m行,那么就相当于告诉了我们有哪几列,还能放2个1,1个1,和不能再放1了. 注意到,这个时候列之间是可以交换的,那么就可以做了. 定义dp[i][j][k]表示在第i行剩j个位置可以放2个1,剩k个位置可以放1个1. 因为下一行一定要放2个1,那么就有3种转移状态: 1:全部选的是放2个的.那么就转移到了dp[i+1][j-2][k+2]

codeforces#983 B. XOR-pyramid (dp)

参考博客:https://www.01hai.com/note/av137952. 题意:首先定义 (b代表一个数组) 给出一个区间,l,r,求它最大的连续子序列的函数值 分析: 定义dp[x][y]为选取x到y这段区间时的函数值 观察发现dp[x][y]=dp[x+1][y]^dp[x][y-1] 代码: #include<bits/stdc++.h> #define ll long long #define pa pair<int,int> using namespace st

Codeforces 803E--Roma and Poker (DP)

原题链接:http://codeforces.com/problemset/problem/803/E 题意:给一个n长度的字符串,其中'?'可以替换成'D'.'W'.'L'中的任意一种,'D'等价于0, 'W'等价于1.'L'等价于-1.输出所有'?'被替换掉后,W和L的数目之差为k,且任意一个[1, i]的子串中W和L数目之差不能等于k. 思路:用DP做.定义bool dp[i][j]代表前i个字符W和L数目之差为j, -k<=j<=k(在数组中范围为[0, 2*k]),那么当str[i]