最长k可重线段集问题
Posted tian-luo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最长k可重线段集问题相关的知识,希望对你有一定的参考价值。
时空限制1000ms / 128MB
题目描述
给定平面 x−O−y 上 n 个开线段组成的集合 I,和一个正整数 k 。试设计一个算法,从开线段集合 I 中选取出开线段集合 S⊆I ,使得在 x 轴上的任何一点 p,S 中与直线 x=p 相交的开线段个数不超过 k,且∑?∣z∣达到最大。这样的集合 S 称为开线段集合 I 的最长 k 可重线段集。∑?∣z∣ 称为最长 k 可重线段集的长度。
对于任何开线段 z,设其断点坐标为 (x0?,y0?) 和 (x1?,y1?),则开线段 z 的长度 ∣z∣ 定义为:|z|=⌊sqrt{(x1-x0)^2+(y1-y0)^2}⌋
对于给定的开线段集合 I 和正整数 k,计算开线段集合 I 的最长 k 可重线段集的长度。
输入输出格式
输入格式:文件的第一 行有 2 个正整数 n 和 k,分别表示开线段的个数和开线段的可重叠数。
接下来的 n 行,每行有 4 个整数,表示开线段的 2 个端点坐标。
输出格式:程序运行结束时,输出计算出的最长 k 可重线段集的长度。
输入输出样例
说明
1≤n≤500
1≤k≤13
题目链接:https://www.luogu.org/problemnew/show/P3357
最大权不相交路径。和这一题很类似。但是这一题会出现线段垂直x轴的情况,这样就会出现自环,而且是负环,所以直接跑费用流会有问题。因此要对每一个点进行拆点,拆成1号点和2号点,具体连边操作为:每个点和下一个点之间连一条费用为0,容量为INF的边。对于某线段,设其在x轴上投影的左右端点为点u和点v,若u=v,则在这个点的1号点和2号点之间连一条费用为边权,容量为1的边,否则在u的2号点和v的1号点之间连一条边。
#include<bits/stdc++.h> #define INF LLONG_MAX/2 #define N 3005 using namespace std; struct ss { int u,v,next; long long flow,cost; }; ss edg[N*15]; int head[N],now_edge=0; void addedge(int u,int v,long long flow,long long cost) { edg[now_edge]=(ss){u,v,head[u],flow,cost}; head[u]=now_edge++; edg[now_edge]=(ss){v,u,head[v],0,-cost}; head[v]=now_edge++; } int spfa(int s,int t,long long &flow,long long &cost) { int vis[N]={0}; vis[s]=1; queue<int>q; q.push(s); long long dis[N]; for(int i=0;i<N;i++)dis[i]=INF; dis[s]=0; int pre[N]={0}; long long addflow[N]={0}; addflow[s]=INF; while(!q.empty()) { int now=q.front(); q.pop(); vis[now]=0; for(int i=head[now];i!=-1;i=edg[i].next) { ss e=edg[i]; if(e.flow>0&&dis[e.v]>dis[now]+e.cost) { dis[e.v]=dis[now]+e.cost; pre[e.v]=i; addflow[e.v]=min(e.flow,addflow[now]); if(!vis[e.v]) { q.push(e.v); vis[e.v]=1; } } } } if(dis[t]==INF)return 0; flow+=addflow[t]; cost+=addflow[t]*dis[t]; int now=t; while(now!=s) { edg[pre[now]].flow-=addflow[t]; edg[pre[now]^1].flow+=addflow[t]; now=edg[pre[now]].u; } return 1; } void mcmf(int s,int t,long long &flow,long long &cost) { while(spfa(s,t,flow,cost)); } void init() { now_edge=0; memset(head,-1,sizeof(head)); } long long x[N][4]; long long LSH[N]; int size_lsh=0; int f(long long x) { return lower_bound(LSH,LSH+size_lsh,x)-LSH+1; } long long dist(long long x[]) { double now=(x[0]-x[2])*(x[0]-x[2])+(x[1]-x[3])*(x[1]-x[3]); now=sqrt(now); return (long long)now; } int main() { init(); int n,k; scanf("%d %d",&n,&k); for(int i=1;i<=n;i++) for(int j=0;j<4;j++) { scanf("%lld",&x[i][j]); if(j%2==0)LSH[size_lsh++]=x[i][j]; } sort(LSH,LSH+size_lsh); size_lsh=unique(LSH,LSH+size_lsh)-LSH; int s=size_lsh*2+1,t=s+1; for(int i=1;i<size_lsh*2;i++)addedge(i,i+1,INF,0); addedge(s,1,k,0); addedge(size_lsh*2,t,INF,0); for(int i=1;i<=n;i++) { int u=min(f(x[i][0]),f(x[i][2])),v=max(f(x[i][0]),f(x[i][2])); long long w=dist(x[i]); if(u!=v)addedge(u*2,v*2-1,1,-w); else addedge(2*u-1,2*v,1,-w); } long long flow=0,cost=0; mcmf(s,t,flow,cost); printf("%lld ",-cost); return 0; }
以上是关于最长k可重线段集问题的主要内容,如果未能解决你的问题,请参考以下文章