题意:要求一最小规模整数集合与给定的一系列连续整数集合[ai,bi]交集规模大于ci。
使用差分约束系统求解,而差分约束系统又可以使用Bellman_Ford求解(其证明参考算法导论),这里只说明构图方法:
- 设S[i]为从0,1,2,…,i与给定的一些列集合交集的个数,所以与[ai,bi]交集的规模为S[b[i]]-S[a[i]-1] >= c[i],edge = {(b[i],a[i]-1),w = -1*c[i]}。
- 又因为S[i]为单调不减函数,有0 <= S[I]-S[I-1] <= 1。edge = {(i,i-1),w = 0},edge = {(i-1,i),w = 1}。
最终结果就是:S[max_b]-S[min_a-1]。 注意的是:因为a[i]可能为0,所以将ai和bi都加一,易知这样是不影响结果的。 算法导论中只是说上面求出的最短路时一个可行解,并没有证明是
最优解。在课后题中要求证明使用Bellman_Ford求出的{max_x-min_x}为最小解,也即题目要求的最优解。待证
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxN 50005
#define maxE 150005
int nodeNum,edgeNum;
int min_a,max_b;
struct edge
{
unsigned short px,py;
int w;
}edges[maxE];
int str2num()
{
int num = 0;
char c;
while((c = getchar())&&(c != ' ')&&(c != '\n')&&(c >= 0))
{
num *= 10;
num += c-'0';
}
return num;
}
int bellman_ford()
{
int i,j;
int dis[maxN];
char IsRelax;
memset(dis,0,sizeof(dis));
for(i = 0;i < nodeNum;i++)
{
IsRelax = false;
for(j = 0;j < edgeNum;j++)
{
if(dis[edges[j].py] > dis[edges[j].px]+edges[j].w)
{
dis[edges[j].py] = dis[edges[j].px]+edges[j].w;
IsRelax = true;
}
}
if(!IsRelax) break;<span style="white-space:pre"> </span>//已经不能松弛则不可能再松弛,所有距离已达最短
}
printf("%d\n",dis[max_b]-dis[min_a-1]);
return 0;
}
int main()
{
int i;
int intervalNum;
int a,b,c;
char edgeVis[maxN];
while(~scanf("%d",&intervalNum))
{
getchar();
edgeNum = 0;
min_a = 500000;
max_b = 0;
memset(edgeVis,0,sizeof(edgeVis));
while(intervalNum--)
{
a = str2num()+1;
b = str2num()+1;
c = str2num();
edges[edgeNum].px = b;
edges[edgeNum].py = a-1;
edges[edgeNum++].w = -1*c;
if(b == a) edgeVis[b] = true;
if(min_a > a) min_a = a;
if(max_b < b) max_b = b;
}
nodeNum = max_b-min_a+1;
for(i = min_a+1;i <= max_b;i++)
{
edges[edgeNum].px = i-1;
edges[edgeNum].py = i;
edges[edgeNum++].w = 1;
if(!edgeVis[i]) <span style="white-space:pre"> </span>//已经有更小权重的边
{
edges[edgeNum].px = i;
edges[edgeNum].py = i-1;
edges[edgeNum++].w = 0;
}
}
bellman_ford();
}
return 0;
}