/* 那么如果在一个未知数定死的情况下,要求其它所有未知数的最小值怎么办? 只要反过来求最长路径就可以了。最长路径中的三角不等式与最短路径中相反: d(v) >= d(u) + w(u, v) 也就是 d(v) - d(u) >= w(u, v) 很丑的代码,有很多事参考了的,对于这个差分约束还不是很懂的 */ #include <iostream>//2601120 2010-07-13 21:34:22 Accepted 1384 312MS 964K 1268 B C++ 悔惜晟 #include <cstdio> #include <cstring> using namespace std; const int N = 50005; const int INF = 0x7f7f7f7f; struct node { int st; int en; int dis; }; int dis[N]; node Egde[N]; bool Bellman(int s, int e, int n) { memset(dis, -1, sizeof(dis)); dis[s] = 0; bool flag; for(int i = s; i <= e; i++) { flag = true; for(int j = 0; j < n; j++) { if(dis[Egde[j].st] != -1 && dis[Egde[j].st] + Egde[j].dis > dis[Egde[j].en]) { dis[Egde[j].en] = dis[Egde[j].st] + Egde[j].dis; flag = false; } } for (int j = s; j < e;j++) { if (dis[j] != -1 && dis[j] > dis[j+1]) { dis[j+1] = dis[j]; flag = false; } } for (int j = e;j > s; j--) { if (dis[j] != -1 && dis[j] - 1 > dis[j-1]) { dis[j-1] = dis[j] -1; flag = false; } } if(flag) break; } printf("%d/n", dis[e]); return flag; } int main() { int n; while(scanf("%d", &n) != EOF) { int s, e; s = INF; e = -INF; for(int i = 0; i < n; i++) { scanf("%d %d %d", &Egde[i].st, &Egde[i].en, &Egde[i].dis); Egde[i].en++; if(Egde[i].st < s) s = Egde[i].st; if(Egde[i].en > e) e = Egde[i].en; } Bellman(s, e, n); } return 0; } 思路: 题目的转换真的非常非常巧妙,让我再来梳理一下。本题的题意是给了我们一些区间,然后告诉每个区间中至少需要取Ci个数。求出满足n个条件的集合C的最少的元素个数。 首先第一个转化,是找到一个合理的表示。用ti表示每一个数,如果有用就是1,否则是0。吧S(i+1)定义成S(i+1)=sigma(tj)(1<=j<=i)也就是。S[i+1]表示从0到i有多少个数是需要的。 因此,题目中的条件可以表示成S[bi+1]>=S[ai]+Ci//至少要Ci个 这与bellman中的松弛操作时很像的。因此可以看成一些点 有D[v]>=D[u]+w(u,v) 上式对任何u成立,所以v应该是里面最大的,若D[v]<D[u]+w(u,v)则D[v]=D[u]+w(u,v) 于是。可以从ai和bi+1连一条线,它的长度是ci 这里只有这些条件还是不够的,还要加上两个使其满足整数性质的条件 1>=s[i+1]-s[i]>=0 有了这么多条件,使其自然构成了一个差分约束系统。 用spfa算法得到一个最长路,第一个到最后一个节点的最长路即是需要求的值。 1 #include<iostream> 2 #include<vector>//for map 3 #include<queue>//for spfa 4 using namespace std; 5 #define MAXN 50010 6 #define pb push_back 7 int dis[MAXN],used[MAXN]; 8 int aa=INT_MAX,bb=-1;//aa最小bb最大 9 struct edge 10 { 11 int p; 12 int len; 13 }tmp; 14 vector<edge>map[MAXN]; 15 16 void spfa() 17 { 18 int i,t; 19 queue<int>Q; 20 for(i=aa;i<=bb;i++) 21 dis[i]=-INT_MAX; 22 dis[aa]=0; 23 used[aa]=1;//先进一个 24 Q.push(aa); 25 while(!Q.empty()) 26 { 27 t=Q.front(); 28 Q.pop(); 29 used[t]=0;//出队列过后,还可能再进 30 int nt=map[t].size(); 31 for(i=0;i<nt;i++) 32 { 33 if(dis[map[t][i].p]<dis[t]+map[t][i].len)//求最长路 34 { 35 dis[map[t][i].p]=dis[t]+map[t][i].len; 36 if(!used[map[t][i].p]) 37 { 38 used[map[t][i].p]=1; 39 Q.push(map[t][i].p); 40 } 41 } 42 } 43 } 44 } 45 int main() 46 { 47 int i,n; 48 scanf("%d",&n); 49 int u,v,w; 50 for(i=1;i<=n;i++) 51 { 52 scanf("%d%d%d",&u,&v,&w); 53 if(u<aa) aa=u; 54 if(v+1>bb) bb=v+1; 55 tmp.len=w; 56 tmp.p=v+1; 57 map[u].pb(tmp); 58 }//添加ci边 59 for(i=aa;i<=bb;i++) 60 { 61 tmp.len=0; 62 tmp.p=i+1; 63 map[i].pb(tmp); 64 tmp.len=-1; 65 tmp.p=i; 66 map[i+1].pb(tmp); 67 }//添加0边和-1边 68 spfa(); 69 printf("%d/n",dis[bb]); 70 return 0; 71 } 72 技术总结:技术上使用了STL的vector让存储图的邻接表非常方便。spfa的时候省事用了STL的queue。基本的操作就是pop(),push(),front(),size(),empty() 还有一个贪心算法:先按后端点排序,把前面的要求越往后放越好,这样后面放的时候就可以利用前面放的结果。贪心为什么是正确的? 转自:http://www.cppblog.com/initiate/archive/2010/04/03/111530.aspx