题意:给出一些区间,求一个集合的长度要求每个区间里都至少有两个集合里的数。
解法:贪心或者差分约束。贪心的思路很简单,只要将区间按右边界排序,如果集合里最后两个元素都不在当前区间内,就把这个区间内的最后两个数加入集合,如果只有一个元素在区间里就加一个,如果两个元素都在区间里就不加。
差分约束系统用来解一个不等式组,只要这个不等式组里的不等式形如x1 - x2 <= c,c为常数就可以用差分约束来解不等式,将每个变量看做点,每个不等式看做边,求一个最短路,那么dis[i]就是不等式的一组解,主要利用的原理就是在最短路问题中:dis[v] <= dis[u] + edge[u][v],而将x1 - x2 <= c进行移项就可以得到以上形式,由于有负权,所以一般用bellmanford或者spfa……不过我不会写spfa……如果产生负环说明无解。
对于本题来说,将每个区间端点都看做点,sum[i]表示集合中小于等于i的数的个数,则对于一个区间[l, r]来说有不等式sum[r] - sum[l - 1] >= 2,转化成上述形式就是sum[l - 1] - sum[r] <= -2,可以看做是点r到点l-1的一条权值为-2的边。除了题中给出的条件,还有一个隐含条件为0 <= sum[i] - sum[i - 1] <= 1,用以上不等式建图后跑最短路,dis[n] - dis[0]即为答案。
差分约束比较复杂……但是主要想练差分约束才做的这题= =但是数据范围略大……跑bellmanford有点难……最后1000ms擦边过的……
代码:
贪心:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string> #include<string.h> #include<math.h> #include<limits.h> #include<time.h> #include<stdlib.h> #include<map> #include<queue> #include<set> #include<stack> #include<vector> #include<iomanip> #define LL long long #define lson l, m, rt << 1 #define rson m + 1, r, rt << 1 | 1 using namespace std; struct node { int l, r; bool operator < (const node &tmp) const { if(r == tmp.r) return l < tmp.l; return r < tmp.r; } }interval[10005]; int main() { int n; while(~scanf("%d", &n)) { for(int i = 0; i < n; i++) scanf("%d%d", &interval[i].l, &interval[i].r); sort(interval, interval + n); int x = -1, y = -1; int ans = 0; for(int i = 0; i < n; i++) { if(interval[i].l <= x) continue; if(interval[i].l <= y) { x = y; y = interval[i].r; ans++; continue; } x = interval[i].r - 1; y = interval[i].r; ans += 2; } cout << ans << endl; } return 0; }
差分约束系统:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string> #include<string.h> #include<math.h> #include<limits.h> #include<time.h> #include<stdlib.h> #include<map> #include<queue> #include<set> #include<stack> #include<vector> #include<iomanip> #define LL long long #define lson l, m, rt << 1 #define rson m + 1, r, rt << 1 | 1 using namespace std; struct node { int u, v, value; node(int u, int v, int value) : u(u), v(v), value(value) {} node() {} }edge[30005]; int minn = 10000000, maxn, m, cnt; int dis[10005]; const int inf = 0x3f3f3f3f; void BellmanFord()//由于不存在无解的数据,不需要判负环 { memset(dis, 0, sizeof dis); dis[minn] = 0; bool flag = true; while(flag)//剪枝,如果本次没有进行松弛,则停止松弛 { flag = false; for(int j = 0; j < cnt; j++) if(dis[edge[j].v] > dis[edge[j].u] + edge[j].value) { flag = true; dis[edge[j].v] = dis[edge[j].u] + edge[j].value; } } } int main() { while(~scanf("%d", &m)) { cnt = 0; for(int i = 0; i < m; i++) { int a, b; scanf("%d%d", &a, &b); minn = min(minn, min(a, b + 1));//记录最小点 maxn = max(maxn, max(a, b + 1));//记录最大点 edge[cnt++] = node(b + 1, a, -2); } for(int i = minn + 1; i <= maxn; i++) { edge[cnt++] = node(i - 1, i, 1); edge[cnt++] = node(i, i - 1, 0); } BellmanFord(); cout << dis[maxn] - dis[minn] << endl; } return 0; }