【网络流24题】No.21 (最长 k 可重区间集问题 最长不相交路径 最大费用流)

【】

  

输入文件示例
input.txt
4 2
1 7
6 8
7 10
9 13

输出文件示例
output.txt
15

【分析】

  直接co题解好了,写得挺全。。

【建模方法】

方法1

按左端点排序所有区间,把每个区间拆分看做两个顶点<i.a><i.b>,建立附加源S汇T,以及附加顶点S’。

1、连接S到S’一条容量为K,费用为0的有向边。
2、从S’到每个<i.a>连接一条容量为1,费用为0的有向边。
3、从每个<i.b>到T连接一条容量为1,费用为0的有向边。
4、从每个顶点<i.a>到<i.b>连接一条容量为1,费用为区间长度的有向边。
5、对于每个区间i,与它右边的不相交的所有区间j各连一条容量为1,费用为0的有向边。

求最大费用最大流,最大费用流值就是最长k可重区间集的长度。

方法2

离散化所有区间的端点,把每个端点看做一个顶点,建立附加源S汇T。

1、从S到顶点1(最左边顶点)连接一条容量为K,费用为0的有向边。
2、从顶点2N(最右边顶点)到T连接一条容量为K,费用为0的有向边。
3、从顶点i到顶点i+1(i+1<=2N),连接一条容量为无穷大,费用为0的有向边。
4、对于每个区间[a,b],从a对应的顶点i到b对应的顶点j连接一条容量为1,费用为区间长度的有向边。

求最大费用最大流,最大费用流值就是最长k可重区间集的长度。

【建模分析】

这个问题可以看做是求K条权之和最大的不想交路径,每条路径为一些不相交的区间序列。由于是最大费用流,两条路径之间一定有一些区间相交,可以看做事相交部分重复了2次,而K条路经就是最多重复了K次。最简单的想法就是把区间排序后,不相交的区间之间连接一条边,由于每个区间只能用一次,所以要拆点,点内限制流量。如果我们改变一下思路,把端点作为网络中的顶点,区间恰恰是特定一些端点之间的边,这样建模的复杂度更小。方法1的边数是O(N^2)的,而方法2的边数是O(N)的,可以解决更大规模的问题。

感觉我的数据错了hhh

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<queue>
  7 #include<cmath>
  8 using namespace std;
  9 #define Maxn 101000
 10 #define INF 0xfffffff
 11
 12 struct node
 13 {
 14     int x,y,f,o,c,next;
 15 }t[Maxn*10];int len;
 16 int first[Maxn];
 17
 18 int mymin(int x,int y) {return x<y?x:y;}
 19 int mymax(int x,int y) {return x>y?x:y;}
 20
 21 void ins(int x,int y,int f,int c)
 22 {
 23     t[++len].x=x;t[len].y=y;t[len].f=f;t[len].c=c;
 24     t[len].next=first[x];first[x]=len;t[len].o=len+1;
 25     t[++len].x=y;t[len].y=x;t[len].f=0;t[len].c=-c;
 26     t[len].next=first[y];first[y]=len;t[len].o=len-1;
 27 }
 28
 29 int st,ed;
 30 queue<int > q;
 31 int dis[Maxn],pre[Maxn],flow[Maxn];
 32 bool inq[Maxn];
 33 bool bfs()
 34 {
 35     while(!q.empty()) q.pop();
 36     // memset(dis,-1,sizeof(dis));
 37     for(int i=1;i<=ed;i++) dis[i]=-INF;
 38     memset(inq,0,sizeof(inq));
 39     q.push(st);dis[st]=0;flow[st]=INF;inq[st]=1;
 40     while(!q.empty())
 41     {
 42         int x=q.front();
 43         for(int i=first[x];i;i=t[i].next) if(t[i].f>0)
 44         {
 45             int y=t[i].y;
 46             if(dis[y]<dis[x]+t[i].c)
 47             {
 48                 dis[y]=dis[x]+t[i].c;
 49                 pre[y]=i;
 50                 flow[y]=mymin(flow[x],t[i].f);
 51                 if(!inq[y])
 52                 {
 53                     inq[y]=1;
 54                     q.push(y);
 55                 }
 56             }
 57         }
 58         inq[x]=0;q.pop();
 59     }
 60     if(dis[ed]>-INF) return 1;
 61     return 0;
 62 }
 63
 64 void output()
 65 {
 66     for(int i=1;i<=len;i+=2)
 67      printf("%d->%d %d %d\n",t[i].x,t[i].y,t[i].f,t[i].c);
 68     printf("\n");
 69 }
 70
 71 int max_flow()
 72 {
 73     int ans=0,sum=0;
 74     while(bfs())
 75     {
 76         sum+=dis[ed]*flow[ed];
 77         ans+=flow[ed];
 78         int now=ed;
 79         while(now!=st)
 80         {
 81             t[pre[now]].f-=flow[ed];
 82             t[t[pre[now]].o].f+=flow[ed];
 83             now=t[pre[now]].x;
 84         }
 85     }
 86     return sum;
 87 }
 88
 89 struct hp
 90 {
 91     int x,id;
 92 }tt[Maxn];int tl=0;
 93 int nx[Maxn],ny[Maxn],ln[Maxn];
 94
 95 bool cmp(hp x,hp y) {return x.x<y.x;}
 96
 97 void init()
 98 {
 99     int n,k;
100     scanf("%d%d",&n,&k);
101     len=0;
102     memset(first,0,sizeof(first));
103     int mx=0;
104     for(int i=1;i<=n;i++)
105     {
106         int x,y;
107         // scanf("%d%d",&x,&y);
108         // ins(x,y,1,y-x);
109         scanf("%d%d",&nx[i],&ny[i]);
110         ln[i]=ny[i]-nx[i];
111         tt[++tl].x=nx[i];tt[tl].id=i;
112         tt[++tl].x=ny[i];tt[tl].id=i+n;
113         // mx=mymax(mx,y);
114     }
115     sort(tt+1,tt+1+tl,cmp);
116     int pp=1;
117     int now=tt[1].x;tt[1].x=1;
118     for(int i=2;i<=tl;i++)
119     {
120         if(tt[i].x!=now) pp++,now=tt[i].x;
121         tt[i].x=pp;
122     }
123     for(int i=1;i<=tl;i++)
124     {
125         if(tt[i].id>n) ny[tt[i].id-n]=tt[i].x;
126         else nx[tt[i].id]=tt[i].x;
127     }
128     for(int i=1;i<=n;i++) ins(nx[i],ny[i],1,ln[i]);
129     mx=pp;
130     st=mx+1;ed=st+1;
131     ins(st,1,k,0);
132     for(int i=1;i<mx;i++) ins(i,i+1,k,0);
133     ins(mx,ed,k,0);
134 }
135
136 int main()
137 {
138     init();
139     // output();
140     int ans;
141     ans=max_flow();
142     printf("%d\n",ans);
143     return 0;
144 }

2016-11-08 07:26:58

时间: 2024-07-29 00:09:06

【网络流24题】No.21 (最长 k 可重区间集问题 最长不相交路径 最大费用流)的相关文章

「网络流24题」「LuoguP3358」 最长k可重区间集问题

题目描述 对于给定的开区间集合 I 和正整数 k,计算开区间集合 I 的最长 k可重区间集的长度. 输入输出格式 输入格式: 的第 1 行有 2 个正整数 n和 k,分别表示开区间的个数和开区间的可重迭数.接下来的 n行,每行有 2 个整数,表示开区间的左右端点坐标. 输出格式: 将计算出的最长 k可重区间集的长度输出 输入输出样例 输入样例#1: 复制 4 2 1 7 6 8 7 10 9 13 输出样例#1: 复制 15 说明 对于100%的数据,1<=n<=500,1<=k<

[网络流 24 题]最长k可重区间集(费用流)

Description 给定实直线L 上n 个开区间组成的集合I,和一个正整数k,试设计一个算法,从开区间集合I 中选取出开区间集合S属于I,使得在实直线L 的任何一点x,S 中包含点x 的开区间个数不超过k,且sum(|z|)z属于S,达到最大.这样的集合S称为开区间集合I的最长k可重区间集.sum(|z|) z属于S称为最长k可重区间集的长度.对于给定的开区间集合I和正整数k,计算开区间集合I的最长k可重区间集的长度. Solution 1.离散化 然后从每个点i向i+1连一条流量为INF,

「网络流 24 题」最长 k 可重区间集

给定区间集合$I$和正整数$k$, 计算$I$的最长$k$可重区间集的长度. 区间离散化到$[1,2n]$, $S$与$1$连边$(k,0)$, $i$与$i+1$连边$(k,0)$, $2n$与$T$连边$(k,0)$. 对于每个区间$(l,r)$, $l$与$r$连边$(1,l-r)$. 最小费用相反数就为最大长度 #include <iostream> #include <sstream> #include <algorithm> #include <cst

网络流 P3358 最长k可重区间集问题

P3358 最长k可重区间集问题 题目描述 对于给定的开区间集合 I 和正整数 k,计算开区间集合 I 的最长 k可重区间集的长度. 输入输出格式 输入格式: 的第 1 行有 2 个正整数 n和 k,分别表示开区间的个数和开区间的可重迭数.接下来的 n行,每行有 2 个整数,表示开区间的左右端点坐标. 输出格式: 将计算出的最长 k可重区间集的长度输出 输入输出样例 输入样例#1: 复制 4 2 1 7 6 8 7 10 9 13 输出样例#1: 复制 15 说明 对于100%的数据,1\le

最长k可重区间集(cogs 743)

?问题描述:?编程任务:对于给定的开区间集合I和正整数k,计算开区间集合I的最长k可重区间集的长度.?数据输入:由文件interv.in提供输入数据.文件的第1 行有2 个正整数n和k,分别表示开区间的个数和开区间的可重迭数.接下来的n行,每行有2个整数,表示开区间的两个端点坐标.?结果输出:程序运行结束时,将计算出的最长k可重区间集的长度输出到文件interv.out中.输入文件示例 输出文件示例interv.in4 21 76 87 10 9 13 interv.out 15 /* 朴素的做

最长k可重区间集问题

最长k可重区间集问题 题目链接 https://www.luogu.org/problemnew/show/3358 做法 所有点向下一个点连容量为k费用为0的边 l和r连容量为1费用为区间长度的边 然后跑最大流最大费用流 (最大费用就是把边权取相反数跑最小费用 最后再输出最终费用的相反数) 思考 在整张图中,只有l - >r的边有费用 而且费用为区间长度 (i->i+1费用为0) 所以跑最大费用也就是求最长区间 #include <algorithm> #include <

P3358 最长k可重区间集问题

P3358 最长k可重区间集问题 题目链接 #include <bits/stdc++.h> using namespace std; typedef pair<int, int> P; struct edge { int to, cap, cost, rev; edge(int a, int b, int c, int d) : to(a), cap(b), cost(c), rev(d) {} }; const int maxn = 510; const int maxv =

洛谷 P3357 最长k可重线段集问题【最大流】

pre:http://www.cnblogs.com/lokiii/p/8435499.html 和最长k可重区间集问题差不多,也就是价值的计算方法不一样,但是注意这里可能会有x0==x1的情况也就是l==r的情况,然后就TTTTTLE. 其实处理方法很粗暴,因为是开线段,所以可以把它扩大一倍,然后就可以取精度差,对于l!=r,l++,否则l--. 然后正常建模即可. 这个建模大概是用了取补集的思想,把覆盖和没覆盖相转化. #include<iostream> #include<cstd

最长k可重线段集问题

最长k可重线段集问题 时空限制1000ms / 128MB 题目描述 给定平面 x−O−y 上 n 个开线段组成的集合 I,和一个正整数 k .试设计一个算法,从开线段集合 I 中选取出开线段集合 S⊆I ,使得在 x 轴上的任何一点 p,S 中与直线 x=p 相交的开线段个数不超过 k,且∑?∣z∣达到最大.这样的集合 S 称为开线段集合 I 的最长 k 可重线段集.∑?∣z∣ 称为最长 k 可重线段集的长度. 对于任何开线段 z,设其断点坐标为 (x0?,y0?) 和 (x1?,y1?),则