建图很巧妙
先离散化区间端点。从0->1,1->2,~~~~~n->n+1每条边容量为k,费用为0,
对于每条线段,他的两个端点连边容量为1,费用-w
然后跑一遍最小费用流
算法正确性证明:
如果两个区间没有交集,那么代表它们的边可以出现在同一增广路上,这一点显然。否则,它们就在不同的增广路上。每一个区间对应的边的容量都是1,这样,最后的流量就是被选出的两两有交集的区间的数量。受到(0,1,k,0)这条边的容量限制,这个值刚好不大于k.区间的权都是正的,因此选取的区间多多益善,所以流量必然最大。
(对于每次选取的增广路中总存在一个区间,在每次增广所得区间都与这个区间有交集)
#include<cstdio> #include<stdlib.h> #include<cstring> using namespace std; const int inf=99999999; struct{ int v, cap, cost, next, re; }edge[2005]; struct{ int a,b,w; }line[210]; struct Li{ int be,num; }li[450]; int n,ans; int k,edgeHead[500]; int que[500],pre[500],dis[500]; bool vis[500]; int cmp(const void * a,const void *b){ struct Li *aa=(struct Li *) a; struct Li *bb=(struct Li *) b; return aa->be-bb->be; } void addEdge(int u,int v,int ca,int co){ edge[k].v=v; edge[k].cap=ca; edge[k].cost=co; edge[k].next=edgeHead[u]; edge[k].re=k + 1; //这个用来记录此边的反边 edgeHead[u]=k ++; edge[k].v=u; edge[k].cap=0; edge[k].cost=-co; //故这里去反费用(因为是反向边) edge[k].next=edgeHead[v]; edge[k].re=k - 1; edgeHead[v]=k ++; } bool spfa(){ //寻找最长增广路时如果是正向边则+相应的费用,反向边-相应的费用 int i, head = 0, tail = 1; // 长注释的地方就是从最小费用改到最大费用时需要变动的地方 for(i = 0; i <= n; i ++){ dis[i] = inf;//////////// vis[i] = false; } dis[0] = 0; que[0] = 0; vis[0] = true; while(head != tail){ int u = que[head]; for(i = edgeHead[u]; i != 0; i = edge[i].next){ int v = edge[i].v; if(edge[i].cap && dis[v] >dis[u] + edge[i].cost){//////// dis[v] = dis[u] + edge[i].cost; pre[v] = i; // 这个pre数组记录的是从边号为i的那条边去往v if(!vis[v]){ vis[v] = true; que[tail ++] = v; if(tail == 490) tail = 0; // 这里用到了循环队列,节省空间 } } } vis[u] = false; head++; if(head ==490) head = 0; } if(dis[n] ==inf) return false;/////////// return true; } void end(){ int u, p,mi; mi=inf; for(u = n; u != 0; u = edge[edge[p].re].v){ p = pre[u]; if(edge[p].cap<mi) mi=edge[p].cap; } for(u = n; u != 0; u = edge[edge[p].re].v){ p = pre[u]; edge[p].cap -= mi; edge[edge[p].re].cap += mi; ans += edge[p].cost*mi; } } int main(){ int i,j,T,t,v,lim,a,b,w; scanf("%d",&T); for(t=1;t<=T;t++){ k=1; memset(edgeHead,0,sizeof(edgeHead)); scanf("%d %d",&v,&lim); for(i=1;i<=v;i++){ scanf("%d %d %d",&a,&b,&w); li[i*2-1].be=a; li[i*2-1].num=-i; li[i*2].be=b; li[i*2].num=i; line[i].a=a; line[i].b=b; line[i].w=w; } qsort(&li[1],2*v,sizeof(li[1]),cmp); int temp=li[1].be,tp=1; for(i=1;i<=v*2;i++){ if(li[i].be!=temp){ tp++; temp=li[i].be; } if(li[i].num>0) //注意这里不能写成if(li[i].num)因为大于0和小于0都满足........在这里T了无数次 line[li[i].num].b=tp; else line[-li[i].num].a=tp; } for(i=0;i<=tp;i++){ addEdge(i,i+1,lim,0); } n=tp+1; for(i=1;i<=v;i++){ addEdge(line[i].a,line[i].b,1,-line[i].w); } ans=0; while(spfa())end(); printf("%d\n",-ans); } return 0; }
用STL更简捷
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int inf=99999999; struct{ int v, cap, cost, next, re; }edge[2005]; struct{ int a,b,w; }line[210]; int li[450]; int n,ans; int k,edgeHead[500]; int que[500],pre[500],dis[500]; bool vis[500]; void addEdge(int u,int v,int ca,int co){ edge[k].v=v; edge[k].cap=ca; edge[k].cost=co; edge[k].next=edgeHead[u]; edge[k].re=k + 1; //这个用来记录此边的反边 edgeHead[u]=k ++; edge[k].v=u; edge[k].cap=0; edge[k].cost=-co; //故这里去反费用(因为是反向边) edge[k].next=edgeHead[v]; edge[k].re=k - 1; edgeHead[v]=k ++; } bool spfa(){ //寻找最长增广路时如果是正向边则+相应的费用,反向边-相应的费用 int i, head = 0, tail = 1; // 长注释的地方就是从最小费用改到最大费用时需要变动的地方 for(i = 0; i <= n; i ++){ dis[i] = inf;//////////// vis[i] = false; } dis[0] = 0; que[0] = 0; vis[0] = true; while(head != tail){ int u = que[head]; for(i = edgeHead[u]; i != 0; i = edge[i].next){ int v = edge[i].v; if(edge[i].cap && dis[v] >dis[u] + edge[i].cost){//////// dis[v] = dis[u] + edge[i].cost; pre[v] = i; // 这个pre数组记录的是从边号为i的那条边去往v if(!vis[v]){ vis[v] = true; que[tail ++] = v; if(tail == 490) tail = 0; // 这里用到了循环队列,节省空间 } } } vis[u] = false; head++; if(head ==490) head = 0; } if(dis[n] ==inf) return false;/////////// return true; } void end(){ int u, p,mi; mi=inf; for(u = n; u != 0; u = edge[edge[p].re].v){ p = pre[u]; if(edge[p].cap<mi) mi=edge[p].cap; } for(u = n; u != 0; u = edge[edge[p].re].v){ p = pre[u]; edge[p].cap -= mi; edge[edge[p].re].cap += mi; ans += edge[p].cost*mi; } } int main(){ int i,j,T,t,v,lim,a,b,w; int cnt; scanf("%d",&T); for(t=1;t<=T;t++){ k=1; cnt=0; memset(edgeHead,0,sizeof(edgeHead)); scanf("%d %d",&v,&lim); for(i=1;i<=v;i++){ scanf("%d %d %d",&a,&b,&w); li[cnt++]=a; li[cnt++]=b; line[i].a=a; line[i].b=b; line[i].w=w; } sort(li,li+cnt); n=unique(li,li+cnt)-li; for(i=1;i<=n;i++) addEdge(i-1,i,lim,0); for(i=1;i<=v;i++){ int x=lower_bound(li,li+n,line[i].a)-li+1; int y=lower_bound(li,li+n,line[i].b)-li+1; addEdge(x,y,1,-line[i].w); } ans=0; while(spfa())end(); printf("%d\n",-ans); } return 0; }