loj6227 「网络流 24 题」最长k可重线段集问题

(http://www.elijahqi.win/2017/12/18/loj6227-%E3%80%8C%E7%BD%91%E7%BB%9C%E6%B5%81-24-%E9%A2%98%E3%80%8D%E6%9C%80%E9%95%BFk%E5%8F%AF%E9%87%8D%E7%BA%BF%E6%AE%B5%E9%9B%86%E9%97%AE%E9%A2%98/%20%E2%80%8E)
题目描述

给定平面 xor\text{xor}xor上n个开线段组成的集合 I\text{I}I,和一个正整数k,试设计一个算法。

从开线段集合I\text{I}I中选取出开线段集合S∈I\text{S}\in \text{I}S∈I ,

使得在x轴上的任何一点 p\text{p}p , S\text{S}S 中与直线 x=p\text{x}=\text{p}x=p 相交的开线段个数不超过 k\text{k}k ,

且 ∑z∈S∣z∣\sum_{\text{z} \in \text{S}}|z|∑​z∈S​​∣z∣ 达到最大。

这样的集合 S\text{S}S 称为开线段集合I\text{I}I 的最长 k\text{k}k 可重线段集的长度。

对于任何开线段z\text{z}z ,设其断电坐标为(x0,y0)( x_0 , y_0 )(x​0​​,y​0​​)和 (x1,y1)( x_1 , y_1 )(x​1​​,y​1​​),

则开线段 z\text{z}z 的长度 ∣z∣|\text{z}|∣z∣定义为: ∣z∣=⌊(x1−x0)2+(y1−y0)2⌋|z| = \lfloor \sqrt{ ( x_1 - x_0 ) ^ 2 + ( y_1 - y_0 )^2 } \rfloor∣z∣=⌊√​(x​1​​−x​0​​)​2​​+(y​1​​−y​0​​)​2​​​​​⌋

对于给定的开线段集合 I\text{I}I 和正整数 k\text{k}k ,计算开线段集合 I\text{I}I 的最长 k\text{k}k 可重线段集的长度。
输入格式

文件的第一 行有二 个正整数 n\text{n}n 和 k\text{k}k ,分别表示开线段的 个数和开线段的可重迭数。接下来的 n\text{n}n 行,每行有 4个整数,表示开线段的2 个端点坐标。
输出格式

程序运行结束时,输出计算出的最长k可重线段集的长度。
样例
样例输入

4 2
1 2 7 3
6 5 8 3
7 8 10 5
9 6 13 9

样例输出

17

数据范围与提示

1≤n≤5001\leq n\leq5001≤n≤500

1≤k≤131 \leq k \leq 131≤k≤13

同之前的最长k可重区间集问题 很像

但是这个建图略有不同 不同在哪里 针对这个 开区间(4,4)也是成立的开区间 但是如果我有自环去跑我的spfa 肯定会tle 怎么办 从Leoly那里get一种新的离散化方式qwq 强制把我两个点扩大一下 然后分成奇数和偶数 这样我就可以强制把一个点拆成两个点了 然后做法和之前一样 首先我需要i,i+1之间连Inf的边 这样我就保证了 我不需要从源点向每个线段连边 然后我从一个线段的左端点要向右端点连边 权值为1 费用为长度 这样保证了我这个线段只被使用一次 然后跑最大费用最大流即可 还要限制一下源点到起点的权值为k才可以

#include<cmath>
#include<queue> 
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
#define N 1100
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x*f;
}
struct node{
    int x,y,next,z,c;
}data[N*N];
inline long long calc(int x1,int y1,int x2,int y2){
    return (long long)(x2-x1)*(x2-x1)+(long long)(y2-y1)*(y2-y1);
}
int num=1,h[N],xx[N],x2[N],yy[N],y2[N],T,pre[N],path[N],n,k,flag[N],qq[N],f[N],w[N];
inline void insert1(int x,int y,int z,int c){
    data[++num].y=y;data[num].next=h[x];data[num].z=z;data[num].c=c;h[x]=num;data[num].x=x;
    data[++num].y=x;data[num].next=h[y];data[num].z=0;data[num].c=-c;h[y]=num;data[num].x=y;
}
inline bool spfa(){
    memset(f,128,sizeof(f));memset(flag,0,sizeof(flag));memset(pre,-1,sizeof(pre));queue<int>q;flag[0]=1;f[0]=0;q.push(0);
    while(!q.empty()){
        int x=q.front();flag[x]=0;q.pop();
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y,z=data[i].z,c=data[i].c;
            if (f[x]+c>f[y]&&z){
                f[y]=f[x]+c;pre[y]=x;path[y]=i;
                if (!flag[y]) flag[y]=1,q.push(y);
            }
        }
    }if (pre[T]==-1) return 0;else return 1;
}
int main(){
    freopen("loj.in","r",stdin);
    n=read();k=read();int tot=0;
    for (int i=1;i<=n;++i){
        xx[i]=read(),yy[i]=read(),x2[i]=read(),y2[i]=read();w[i]=sqrt(calc(xx[i],yy[i],x2[i],y2[i]));
        xx[i]=xx[i]<<1|1;x2[i]<<=1;qq[++tot]=xx[i];qq[++tot]=x2[i];if (xx[i]>x2[i]) swap(xx[i],x2[i]);
    }sort(qq+1,qq+tot+1);int nn=unique(qq+1,qq+tot+1)-qq-1;T=nn+1;//printf("%d\n",nn);
    for (int i=1;i<nn;++i) insert1(i,i+1,inf,0);
    for (int i=1;i<=n;++i){
        int l=lower_bound(qq+1,qq+nn+1,xx[i])-qq,r=lower_bound(qq+1,qq+nn+1,x2[i])-qq;//printf("%d %d %d\n",l,r,w[i]);
        insert1(l,r,1,w[i]);
    }insert1(0,1,k,0);insert1(nn,T,inf,0);int ans=0;
//  for (int i=2;i<=num;++i) printf("%d %d %d %d\n",data[i].x,data[i].y,data[i].z,data[i].c);
    while(spfa()){
        int minn=inf,now=T;
        while(now) minn=min(minn,data[path[now]].z),now=pre[now];now=T;
        while(now){ans+=minn*data[path[now]].c;data[path[now]].z-=minn;data[path[now]^1].z+=minn;now=pre[now];}
    }printf("%d",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值