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

最大费用最大流好题,题目难懂, 构图难想,代码难打(类似最长k可重区间集问题)!

请先阅读最长k可重区间集问题,再往下看(不然看不懂我在讲什么)

这里就只将不同点了!

这一道题长度的计算方法(流量)不同,且有特殊情况:线段(这一理解为)垂直于x坐标轴,需要特判(重点&难点)!

(而且我们可以知道线段的纵坐标除了用来计算长度,就没有什么用了,因为题目求的是S 中与直线 x=px=p 相交的开线段个数

方法1:

关于构图:

计算方法就不讲了,在构建蓝色边时,需要判断当点i和点j同时垂直x坐标轴且x坐标相等时,不能相连。


关于代码(只有91分,还有一个点没有调出来,先留一个坑,请你帮我填)

#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<queue>
#include<algorithm>
#define INF 2147483647
using namespace std;
queue<int> f;
	int n,k,len=-1,st,ed;
	struct node1{int x,y,c,d,next;} a[100000];
	struct node2{int x1,y1,x2,y2,z;} b[50000];
	int last[100000],dis[100000],pre[100000],pos[100000],p[100000];
	bool bz[100000];
bool cmp(node2 x,node2 y)
{
	return x.x1<y.x1;
}
bool pd(int x,int y)
{
	return !(b[x].x1<b[y].x2)||(b[x].x1==b[x].x2&&b[y].x1==b[y].x2&&b[x].x1==b[y].x1);
}
void ins(int x,int y,int c,int d)
{
	a[++len].x=x;a[len].y=y;a[len].c=c;a[len].d=d;a[len].next=last[x];last[x]=len;
}
bool spfa()
{
	memset(bz,true,sizeof(bz));
	bz[st]=false;
	memset(dis,63,sizeof(dis));
	dis[st]=0;
	p[st]=INF;
	f.push(st);
	while(!f.empty())
	{
		int x=f.front();
		bz[x]=true;
		for(int i=last[x];i>=0;i=a[i].next)
		{
			int y=a[i].y;
			if(a[i].c>0&&dis[y]>dis[x]+a[i].d)
			{
				dis[y]=dis[x]+a[i].d;
				pos[y]=x;
				pre[y]=i;
				p[y]=min(p[x],a[i].c);
				if(bz[y])
				{
					f.push(y);
					bz[y]=false;
				}
			}
		}
		f.pop();
	}
	return dis[ed]<1061109567;
}
int flow()
{
	int ans=0;
	while(spfa())
	{
		ans+=p[ed]*dis[ed];
		for(int i=ed;i!=st;i=pos[i])
		{
			a[pre[i]].c-=p[ed];
			a[pre[i]^1].c+=p[ed];
		}
	}
	return ans;
}
int main()
{
	int idx,idy;
	scanf("%d %d",&n,&k);
	st=0,ed=1+n*2+1;
	memset(last,-1,sizeof(last));
	ins(st,st+1,k,0),ins(st+1,st,0,0);
	for(int i=1;i<=n;i++)
	{
		scanf("%d %d %d %d",&b[i].x1,&b[i].y1,&b[i].x2,&b[i].y2);
		b[i].z=sqrt(pow(b[i].x1-b[i].x2,2)+pow(b[i].y1-b[i].y2,2));
	}
	sort(b+1,b+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		idx=1+i*2-1,idy=1+i*2;
		ins(st+1,idx,1,0),ins(idx,st+1,0,0);
		ins(idy,ed,1,0),ins(ed,idy,0,0);
		ins(idx,idy,1,-b[i].z),ins(idy,idx,0,b[i].z);
		for(int j=1;j<i;j++)
			if(pd(i,j)) ins(1+j*2,idx,1,0),ins(idx,1+j*2,0,0);
	}
	printf("%d",-flow());
}


方法2:

关于构图:

当点i和点j同时垂直x坐标轴且x坐标相等时,我们需要它们不建边(不然会跑负环),所以在离散化的时候不能放在一起,因此我们需要特判:

1.先将点i的两个横坐标都扩大两倍(不理解,当然了,先看下面);

2.当点i垂直于x坐标轴时(即点i的两个横坐标相等),点i第一个横坐标-1,反之+1。

为什么这样不会有影响呢?因为这样一来就不会形成环,并且没有任何影响(相当于在x轴上多加了点) 。


关于代码:

#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<queue>
#include<algorithm>
#define INF 2147483647
using namespace std;
queue<int> f;
	int n,k,len=-1,st,ed;
	struct node{int x,y,c,d,next;} a[100000];
	int b[1000][5],value[1000],list[2000],last[100000],dis[100000],pre[100000],pos[100000],p[100000];
	bool bz[100000];
void ins(int x,int y,int c,int d)
{
	a[++len].x=x;a[len].y=y;a[len].c=c;a[len].d=d;a[len].next=last[x];last[x]=len;
}
bool spfa()
{
	memset(bz,true,sizeof(bz));
	bz[st]=false;
	memset(dis,63,sizeof(dis));
	dis[st]=0;
	p[st]=INF;
	f.push(st);
	while(!f.empty())
	{
		int x=f.front();
		bz[x]=true;
		for(int i=last[x];i>=0;i=a[i].next)
		{
			int y=a[i].y;
			if(a[i].c>0&&dis[y]>dis[x]+a[i].d)
			{
				dis[y]=dis[x]+a[i].d;
				pos[y]=x;
				pre[y]=i;
				p[y]=min(p[x],a[i].c);
				if(bz[y])
				{
					f.push(y);
					bz[y]=false;
				}
			}
		}
		f.pop();
	}
	return dis[ed]<1061109567;
}
int flow()
{
	int ans=0;
	while(spfa())
	{
		ans+=p[ed]*dis[ed];
		for(int i=ed;i!=st;i=pos[i])
		{
			a[pre[i]].c-=p[ed];
			a[pre[i]^1].c+=p[ed];
		}
	}
	return ans;
}
int main()
{
	int x,y,t=0,m;
	scanf("%d %d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d %d %d %d",&b[i][1],&b[i][2],&b[i][3],&b[i][4]);
		value[i]=sqrt(pow(b[i][1]-b[i][3],2)+pow(b[i][2]-b[i][4],2));
		b[i][1]<<=1,b[i][3]<<=1;
		b[i][1]+=b[i][1]!=b[i][3]?1:-1;
		list[++t]=b[i][1],list[++t]=b[i][3];
	}
	sort(list+1,list+t+1);
	m=unique(list+1,list+t+1)-list-1;
	st=0,ed=m+1;
	memset(last,-1,sizeof(last));
	ins(st,1,k,0),ins(1,st,0,0);
	ins(m,ed,k,0),ins(ed,m,0,0);
	for(int i=1;i<m;i++)
		ins(i,i+1,INF,0),ins(i+1,i,0,0);
	for(int i=1;i<=n;i++)
	{
		x=lower_bound(list+1,list+1+m,b[i][1])-list;
		y=lower_bound(list+1,list+1+m,b[i][3])-list;
		ins(x,y,1,-value[i]),ins(y,x,0,value[i]);
	}
	printf("%d",-flow());
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值