最大费用最大流好题,题目超难懂, 构图超难想,代码超难打(超类似最长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());
}