一道费用流练习题

题意

在平面上有N条平行于Y轴的线段,每条线段都有一个权值,每次可以找出的Y轴上投影互不重叠的若干线段,求M次后,找出的线段最大权值和为多少。(每条线段只能贡献一次)

数据范围

对于30%的数据,N<=100;M<=2;1<=Y1<Y2<=501;
对于50%的数据,P=1;
对于100%的数据,N<=1000;M<=100;1<=Y1<Y2<=5001;1<=P<=100000;1<=X<=5001。

解法

考虑费用流,找m次可以当做总流量为m,然后首先的暴力方法是对于每条线段,与它互不重叠的线段连边,这样边数是 O ( n 2 ) O(n^2) 的,并不能过.
然后这里好像算是一个常见技巧:对数轴建边,注意到由于是在y轴投影上互不重叠,所以x坐标是没有用的.所有的坐标只有 O ( 5000 ) O(5000) 级别,所以我们建出(i,i+1,inf,0)这种边,形成纵坐标轴,然后把原来的线段,流量为1,费用是权值,直接建在轴上,跑最大费用最大流.

考虑这样的正确性,一条线段被选了以后,一条和它不相交的线段不会多增加流量,而一条和它相交的边则会增加流量.所以正确.

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e4+5;
const int inf=0x3f3f3f3f3f3f3f3fll;
inline int read(){
	char c=getchar();int t=0,f=1;
	while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n,m,s;
struct edge{
	int v,p,w,c;
}e[maxn<<2];
int h[maxn],cnt=1;
inline void add(int a,int b,int c,int d){
	e[++cnt].p=h[a];
	e[cnt].v=b;
	e[cnt].w=c;
	e[cnt].c=d;
	h[a]=cnt;
}
int cost,dis[maxn],vis[maxn],ht[maxn],t;
bool bfs(){
	//printf("%d\n",s);
	queue<int> q;
	while(!q.empty())q.pop();
	memset(dis,-1,sizeof(dis));memset(vis,0,sizeof(vis));
	q.push(s);
	dis[s]=0;int st=dis[t];
	while(!q.empty()){
		int u=q.front();q.pop();vis[u]=0;
		//printf("%d\n",u);
		for(int i=h[u];i;i=e[i].p){
			int v=e[i].v;
			//printf("%d %d\n",u,v);
			if(e[i].w&&dis[v]<dis[u]+e[i].c){
				dis[v]=dis[u]+e[i].c;
				if(!vis[v]){
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
	return dis[t]!=st;
}
int dfs(int u,int rest){
	if(rest==0)return rest;
	if(u==t){vis[u]=1;return rest;}
	vis[u]=1;
	int tot=0;
	for(int &i=ht[u];i;i=e[i].p){
		int v=e[i].v;
		if(e[i].w&&(!vis[v])&&dis[v]==dis[u]+e[i].c){
			int di=dfs(v,min(rest,e[i].w));
			e[i].w-=di;e[i^1].w+=di;
			rest-=di;tot+=di;cost+=di*e[i].c;
			if(rest==0)break;
		}
	}
	return tot;
}
void dinic(){
	while(bfs()){
		vis[t]=1;
		for(int i=1;i<=s;i++)ht[i]=h[i];
		while(vis[t]){
			memset(vis,0,sizeof(vis));
			dfs(s,inf);
		}
	}
}
signed main(){
//	freopen("3.in","r",stdin);
//	freopen("3.out","w",stdout);
	n=read(),m=read();s=5e3+6;t=5e3+5;
	add(s,1,m,0);add(1,s,0,0);
	for(int i=1;i<=n;i++){
		int x=read(),y1=read(),y2=read(),p=read();
		if(y1>y2)swap(y1,y2);
		add(y1,y2,1,p);add(y2,y1,0,-p);
	}
	//printf("%d\n",s);
	for(int i=1;i<5e3+5;i++){
		add(i,i+1,inf,0);
		add(i+1,i,0,0);
	}
	//printf("%d\n",s);
	dinic();
	printf("%lld\n",cost);
	return 0;
}

发布了95 篇原创文章 · 获赞 9 · 访问量 3204

猜你喜欢

转载自blog.csdn.net/wmhtxdy/article/details/103958669