这是我的第五道“差分约束系统”;,值得庆祝的是,本题完全是自己做出来的,没有参考任何资料,呵呵.....兴奋......
题意理解:(题意有点费解,看了几遍才懂)第一行输入n,下面输入n个限制条件,条件的格式为 ai bi ci, 0<=ai<=bi<=50000,1<=ci<=bi-ai+1.表示在线段[ai,bi]上至少选ci个点,使被选出的点的个数最少而且满足所有的限制条件,输出这个最小值。
题目分析:
1. 开始我用每个整数(1,2,...)当做图的结点,添加边就是 add(b, a, -c),写出来之后发现连题目的样例数据都输出错误结果8.
2. 研究了一番发现这样不行,比如有两个限制条件分别为:1 3 2, 3 6 2; 那么按照上面的思路添加这两条边<3,1>=-2, <6,3>=-2; 后又可以得到边 <6,1>=-4, 意思是在线段[1,6]上至少要选4个点,而实际上不是的,应该是至少要选3个点才对,问题出哪呢??因为线段[1,3]和[3,6]有一个公共点3.
3. 所以要换一种方式建图,对于一个条件(ai,bi,ci)实际上表示在开区间(ai-0.5, bi+0.5)上至少选ci个整数点,但是又不能把 3.5,4.5,5.5...这样的小数当做图的结点啊!再换一种,用左闭右开区间[ai, bi+1),因为取的是整数点,所以意义和闭区间[ai,bi]一样,这样,对于每个限制条件(ai,bi,ci)就可以添加边<bi+1, ai>=-ci;即 add(b+1,a,-c).
4. 仅仅添加题目给的限制条件是不够的,题目只给了每条线段上至少选的点个数,但对于线段[a,b]还有一个隐藏的最多可选点数为b-a+1个,所以要 add(a,b+1,b-a+1).
5. 为了保证图的连通性还必须添加每相邻两个整数点i,i+1的边,区间[i,i+1)上至少有0个,至多有1个,所以add(i+1,i,0); add(i,i+1,1).
6. 大功告成,样例数据正确,提交....悲剧....TLE....
7. 难道又和poj 3159 一样,把每条有向边的顺序改变一下,即把每个add(a,b,c)改成add(b,a,c)就能节约好几倍的时间,改过之后再提交....悲剧......还是TLE
8. 终于发现有些边是多余的,第4点中的add(a,b+1,b-a+1)是多余的,因为后面有了add(i,i+1,1),去掉add(a,b+1,b-a+1),再提交.....Accept.....
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXV 50005 #define MAXE 200100 #define INF 100000000 struct { int e, v; } edge[MAXE]; int minV, maxV; int neg; int dis[MAXV]; int node[MAXV], next[MAXE]; int Q[MAXV], vst[MAXV]; void add(int s, int e, int v) { edge[neg].e = e; edge[neg].v = v; next[neg] = node[s]; node[s] = neg++; } int relax(int s, int e, int v) { if (dis[s]+v < dis[e]) { dis[e] = dis[s]+v; return 1; } return 0; } int SPFA(int s0) { int i, t, p, top; for (i=minV; i<=maxV; i++) dis[i] = INF; dis[s0] = 0; Q[0] = s0; top = 1; vst[s0] = 1; while (top) { t = Q[--top]; vst[t] = 0; p = node[t]; while (p != -1) { if (relax(t, edge[p].e, edge[p].v) && !vst[edge[p].e]) { Q[top++] = edge[p].e; vst[edge[p].e] = 1; } p = next[p]; } } return 1; } int main() { int n, a, b, c, i; memset(node, -1, sizeof(node)); minV = 50000; maxV = 0; scanf("%d", &n); while (n--) { scanf("%d %d %d", &a, &b, &c); add(b+1, a, -c); //add(a, b+1, b-a+1); //原来这句多余了,加上这句就TLE了 if (minV > a) minV = a; if (maxV < b+1) maxV = b+1; } for (i=minV; i<maxV; i++) { add(i+1, i, 0); add(i, i+1, 1); } SPFA(maxV); printf("%d/n", -dis[minV]); }