POJ 3680

原创 2012年03月21日 18:11:33

建图很巧妙

先离散化区间端点。从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;
}



 

poj3680 Intervals

费用流,构图方法好
  • AaronGZK
  • AaronGZK
  • 2015年12月12日 20:03
  • 1331

解题报告 之 POJ3680 Intervals

解题报告 之 POJ 3680 Intervals 最小费用流 标号法 ACM 负权边 You are given N weighted open intervals. The ith interva...
  • maxichu
  • maxichu
  • 2015年05月02日 10:18
  • 1523

POJ 3680 最小费用最大流

思路:这题建图比较机智,我刚开始想到能建的图也就是离散化后两个端点连边,流量为1,费用为负的权值(因为求的是最大费用最大流),然后再加上源点和汇点,也就如此而已;但是这样建图样例第二和第四个不正确,因...
  • u011466175
  • u011466175
  • 2014年08月26日 14:43
  • 1024

POJ3680-费用流

/* 都说这题是构图很巧的好题~我也赞一个吧,虽然我没有想到。 说说我的思考过程吧。 做这题是因为这题在网络流的分类里面,自然一开始就想构图了~ (1)能作为结点的东西的只有两个,区间和端点,区间没什...
  • yihuikang
  • yihuikang
  • 2012年08月05日 23:52
  • 1422

poj 3680 Intervals(离散化+费用流)

题目链接:http://poj.org/problem?id=3680 题意: 给定n个带权开区间,选择其中一些区间出来,使得权值最大并且在任意被选区间的有效点上重叠层数不超过k。 解题思路: ...
  • hexianhao
  • hexianhao
  • 2016年08月02日 20:32
  • 288

BZOJ 3680 吊打XXX 模拟退火

首先这题应该改名叫吊打出题人 题目大意:给定n个质点,求重心 这n个质点的重心满足Σ(重心到点i的距离)*g[i]最小 模拟退火的裸题 尼玛交了两篇 死活过不去 各种改参数 最后发现是我的IN...
  • PoPoQQQ
  • PoPoQQQ
  • 2014年09月16日 20:17
  • 3301

poj 3680 intervals

在费用流建模上完全是小白,害得你li
  • zzblac
  • zzblac
  • 2014年09月29日 20:10
  • 269

POJ 3680 Intervals(最小费用流)

先离散化所有点,然后排序,然后从左到右,每个xiang
  • kep159
  • kep159
  • 2014年08月17日 15:45
  • 234

[费用流] POJ 3680 Intervals

简单的费用流建模 一个区间ab 从a到b连边 然后从源到汇一路串起来 源汇容量K #include #include #include #include #define cl(x) memset(x,...
  • u014609452
  • u014609452
  • 2017年01月08日 09:34
  • 266

poj 3680 最小费用流

题意:n个区间,每个区间都有权值,尽可能的去取区间,但要保证每个点不被取超过k次。 题解: 主要就是建图:我是不会建。。我太笨了。 首先,我们不可能每个点使其都存在。太多了。所以我们就取每个区间...
  • onepiecehuiyu
  • onepiecehuiyu
  • 2012年10月22日 20:11
  • 276
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:POJ 3680
举报原因:
原因补充:

(最多只允许输入30个字)