cunzai_zsy0531

关注我

P3357 最长k可重线段集问题 题解

Post time: 2020-03-04 18:59:51

题目链接

网络流题目,关键在于建模。

这题是个最大费用最大流,最大流保证k可重,最大费用就是题目求的最长的长度。

建模:

  1. 对于输入的 \(x_i\)\(y_i\),我们计算此线段的长度 \(Len_i\)\(Len_i=\sqrt {x^2_i+y^2_i}\)

  2. \(S\to 1\)\(k\),费用 \(0\)

  3. \(i-1\to i\),流 \(k\),费用 \(0\)

  4. \(i\to j(i\le j)\) 因为可能出现 \(x\) 坐标相同(线段平行于 \(y\) 轴)的情况,所以这里要拆点,每个点拆为 \(2i-1\)\(2i\)。我们设这两个点为 \(X_i\)\(Y_i\)。将 \(i\)\(j\) 离散化之后:若 \(i=j\),那么连 \(X_i\)\(Y_i\);若 \(i\neq j\),连 \(Y_i\)\(X_j\)。流都是 \(k\),费用都是 \(Len_i\)

  5. \(X_n\),\(Y_n\)\(t\),流 \(k\),费用 \(0\)

建模毕。

然后就是 mcmf 板子,注意最大费用最大流最好是边权取反然后跑正常的,但是本人比较懒就直接在SPFA中换了个符号:

if(f&&dist[v]>dist[u]+w)

这里把 > 直接改了 <,数据小的话应该没问题。

注意初始化要改成

memset(dist,0xcf,sizeof(dist));

其他没什么了,上代码:

点击查看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
const int N=1000+5,INF=0x3f3f3f3f;
struct Edge{int u,v,f,w,nxt;}e[N*N];
int h[N],flow[N],dist[N],pre[N],last[N];
bool vis[N];
int l[N],r[N],a[N*2],len[N];
int n,m,s,t,k,tot=1,mc,mf;
inline void add(int u,int v,int f,int w){//建图,建双向边
	e[++tot]=(Edge){u,v,f,w,h[u]};h[u]=tot;
	e[++tot]=(Edge){v,u,0,-w,h[v]};h[v]=tot;
}
bool spfa(){
	memset(dist,0xcf,sizeof(dist));//跑最大费用,dist要赋无穷小
	memset(vis,0,sizeof(vis));
	memset(flow,0x3f,sizeof(flow));
	queue<int>q;
	q.push(s);
	dist[s]=0;
	vis[s]=1;
	pre[t]=-1;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=h[u];i;i=e[i].nxt){
			int v=e[i].v,f=e[i].f,w=e[i].w;
			if(f&&dist[v]<dist[u]+w){//这里改了符号
				dist[v]=dist[u]+w;
				flow[v]=min(flow[u],f);
				pre[v]=u;
				last[v]=i;
				if(!vis[v]){
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
	return pre[t]!=-1;
}
void mcmf(){//mcmf板子
	while(spfa()){
		mf+=flow[t];
		mc+=flow[t]*dist[t];
		int now=t;
		while(now!=s){
			e[last[now]].f-=flow[t];
			e[last[now]^1].f+=flow[t];
			now=pre[now];
		}
	}
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1,y1,y2;i<=n;++i){
		scanf("%d%d%d%d",&l[i],&y1,&r[i],&y2);
		if(l[i]>r[i]) swap(l[i],r[i]);
		len[i]=sqrt(pow(y1-y2,2)+pow(r[i]-l[i],2));//计算Len[i]
		l[i]<<=1,r[i]<<=1;
		if(l[i]==r[i]) r[i]++;
		else l[i]++;
		a[i]=l[i],a[i+n]=r[i];
	}
    //下面是拆点
	sort(a+1,a+2*n+1);
	m=unique(a+1,a+2*n+1)-a-1;s=m+1,t=m+2;
	for(int i=1,L,R;i<=n;++i){
		L=lower_bound(a+1,a+m+1,l[i])-a;
		R=lower_bound(a+1,a+m+1,r[i])-a; 
		add(L,R,1,len[i]);
	}
	for(int i=1;i<=m;++i){
		if(i==1) add(s,i,k,0);
		else{
			add(i-1,i,k,0);
			if(i==m) add(i,t,k,0),add(i,t,k,0);
		}
	}
	mcmf();
	printf("%d\n",mc);//输出最大费用
	return 0;
}
posted @ 2022-04-21 13:58  cunzai_zsy0531  阅读(25)  评论(0编辑  收藏  举报