洛谷P3358 最长k可重区间集问题

题目大意:

在一段区间中存在一些线段

要求从中选出一些线段,使得线段每个点被线段覆盖次数不超过\(k\)次,且线段长度最长

最大费用最大流

我们可以由\(S\)向区间的起点连边,终点向\(T\)连边,容量为\(k\),费用为\(0\),保证了每个点被经过不超过\(k\)

每个点向下一个点连边,容量\(k\),花费\(0\)

每个区间左断点向右端点连边,容量\(1\),花费为区间长度

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int x=0,f=1;
    char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') f=0,ch=getchar();
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return f?x:-x;
}
const int inf=0x3f3f3f3f;
int n,k,ret,st,ed;
int tx[1010],ty[1010],s[1010];
int head[100010],cnt=1;
struct point
{
    int nxt,to,c,val;
}a[200010];
inline void add(int x,int y,int c,int val)
{
    a[++cnt].nxt=head[x];
    a[cnt].to=y;
    a[cnt].c=c;
    a[cnt].val=val;
    head[x]=cnt;
}
inline void adds(int x,int y,int c,int val)
{
    add(x,y,c,val);add(y,x,0,-val);
}
queue<int> q;
int dis[100010],c[100010],pre[100010],eg[100010];
bool vis[100010];
inline bool spfa()
{
    memset(c,0x3f,sizeof(c));
    memset(dis,-0x3f,sizeof(dis));dis[st]=0;
    memset(vis,0,sizeof(vis));vis[st]=1;
    q.push(st);pre[ed]=0;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=head[now];i;i=a[i].nxt)
        {
            int t=a[i].to;
            if(a[i].c&&dis[t]<dis[now]+a[i].val)
            {
                pre[t]=now;
                eg[t]=i;
                dis[t]=dis[now]+a[i].val;
                c[t]=min(c[now],a[i].c);
                if(!vis[t])
                {
                    vis[t]=1;
                    q.push(t);
                }
            }
        }
    }
    return pre[ed];
}
inline int dinic()
{
    while(spfa())
    {
        ret+=dis[ed]*c[ed];
        int now=ed;
        while(now^st)
        {
            a[eg[now]].c-=c[ed];
            a[eg[now]^1].c+=c[ed];
            now=pre[now];
        }
    }
    return ret;
}
signed main()
{
    n=read(),k=read();
    for(int i=1;i<=n;++i)
    {
        tx[i]=read(),ty[i]=read();
        s[++s[0]]=tx[i],s[++s[0]]=ty[i];
    }
    sort(s+1,s+s[0]+1);
    s[0]=unique(s+1,s+s[0]+1)-s-1;//离散
    st=s[0]+1,ed=s[0]+2;
    for(int i=1;i<s[0];++i) adds(i,i+1,k,0);//每个点向右边连0边
    adds(st,1,k,0);adds(s[0],ed,k,0);//起点终点,k个流限制每个点走k次
    for(int x,y,i=1;i<=n;++i)
    {
        x=lower_bound(s+1,s+s[0]+1,tx[i])-s;
        y=lower_bound(s+1,s+s[0]+1,ty[i])-s;
        adds(x,y,1,ty[i]-tx[i]);//走线段
    }
    printf("%d\n",dinic());//最大费用最大流
return 0;
}

猜你喜欢

转载自www.cnblogs.com/knife-rose/p/12095799.html