题目链接:http://poj.org/problem?id=1201
差分约束系统。
我们用s[i]代表从[0,i]所含有的元素和
在本题中,如果[a,b]中有c个元素,那么:
s[b]-s[a-1]>=c,我们可以推得:s[a-1] - s[b] <= -c
同时,由于每一个值上最多只能含有一个元素,那么:
s[i] - s[i-1]<=1
s[i] - s[i-1]>=0 推得:s[i-1] - s[i] <=0
这样:我们有了三个约束不等式:
s[a-1] - s[b] <= -c
s[i] - s[i-1]<=1
s[i-1] - s[i] <=0
于是:
假设题目中所有查询的所在区间范围是:[start,end]的话,那么:
我们只要求出:s[end] -s[start-1] >= M就可以了。
其中M就是我们要的最小值。
我们可以整理成我们容易求解的形式:即:s[start-1] - start[end] <= -M
就是求从end这一点,到start-1这一点的最短路径。
然后求出的最短路径求相反数就是M了。
注意题目要求a可以为0,所有都++避免下标为负。Bellmanford会超时,spfa是首选。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <stack> #include <queue> #include <algorithm> #include <iostream> using namespace std; #define Maxn 50005 #define Maxm 200005 #define INF 0x3f3f3f3f int first[Maxn]; int next[Maxm]; int total; struct Edge { int a,b; int w; }edge[Maxm]; int dist[Maxn]; int vis[Maxn]; void init() { total = 0; memset(first,-1,sizeof(first)); } void addEdge(int a,int b,int w) { edge[total].a = a,edge[total].b = b,edge[total].w = w; next[total] = first[a]; first[a] = total++; } /* bool bellmanFord(int start,int pointNum,int m) { memset(dist,0x3f,sizeof(dist)); dist[start] = 0; for(int i=0;i<pointNum-1;i++) { for(int j=0;j<m;j++) { int a = edge[j].a; int b = edge[j].b; int w = edge[j].w; if(dist[a] + w < dist[b]) dist[b] = dist[a] + w; } } for(int i=0;i<m;i++) { int a = edge[i].a; int b = edge[i].b; int w = edge[i].w; if(dist[a] + w < dist[b]) { return false; } } return true; }*/ int cnt[Maxn]; bool spfa(int s,int n) { memset(dist,0x3f,sizeof(dist)); memset(vis,0,sizeof(vis)); memset(cnt,0,sizeof(cnt)); dist[s] = 0; queue<int> q; q.push(s); vis[s] = 1; while(!q.empty()) { int temp = q.front(); q.pop(); vis[temp] = 0; for(int i=first[temp];i!=-1;i=next[i]) { int a = edge[i].a; int b = edge[i].b; int w = edge[i].w; if(dist[a] + w < dist[b]) { dist[b] = dist[a] + w; if(!vis[b]) { vis[b] = 1; cnt[b]++; if(cnt[b] > n) return false; q.push(b); } } } } //判负环 return true; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif int n; int a,b,c; int start,end; while(scanf(" %d",&n)!=EOF) { init(); start = INF,end = -INF; for(int i=1;i<=n;i++) { scanf(" %d %d %d",&a,&b,&c); a++,b++; start = min(start,a); end = max(end,b); //b-a>=c -> a-b<=-c addEdge(b,a-1,-c); } for(int i=start;i<=end;i++) { //Si - Si-1 <= 1 addEdge(i-1,i,1); //Si-1 - Si <= 0 addEdge(i,i-1,0); } //if(!bellmanFord(end,end-start+2,total)) continue; if(!spfa(end,end-start+2)) continue; int ans = -dist[start-1]; printf("%d ",ans); } return 0; }