POJ 3680
来源:互联网 发布:java 整形转string 编辑:程序博客网 时间:2024/06/06 04:24
建图很巧妙
先离散化区间端点。从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;elseline[-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;}
- POJ 3680
- POJ 3680 Intervals
- poj 3680 intervals
- POJ-3680-Intervals
- poj 3680 Intervals
- POJ 3680 Intervals
- POJ 3680 Intervals
- poj 3680 intervals
- POJ 3680_Intervals
- POJ 3680 Intervals
- POJ 3680 费用流
- poj 3680 费用流
- POJ 3680 Intervals 笔记
- POJ 3680 Intervals
- POJ
- poj
- POJ
- POJ
- 程序员犯的非技术错误(Top 5)
- Java集合类
- 有关nginx日志分析的一点记录
- 程设作业:MyString类
- java中常见类型的转换
- POJ 3680
- 开发人员必学的5门课程
- COCOS2D坐标系统的一些说明
- CG Independent Study PCL learn PCL 01 what is it?
- 查看当前人的前面还有几人 SQL
- 时钟芯片DS1302可靠起振的方法
- 开源资源大汇总(C#)
- 成功人士都是这样逼出来的
- 请教define,const,typedef,inline的区别