[2018雅礼省选集训3-30]path 平面图最短路最小割

根据题意,题目给定的图是一个平面图。如果把 1 1 号点到不了的点和到不了n号点的点及所连边删掉,那么图就大概是有一条 1 1 n的链,然后还有一些边覆盖在上面,这样的话它的对偶图就大概是一棵树。
再考虑颜色限制怎么搞,假设 x x 与其父亲的边在原图中代表边(u,v),那么 x x T割就表示不选 u+1,...,v1 u + 1 , . . . , v − 1 这些点的颜色,于是对于每个颜色建一个点,然后 x x u+1,...,v1的颜色的点连容量正无穷的双向边。注意到 x x T割,那么其子树都在 T T 割,因此x只需要向未被更底层边覆盖的点就可以了。
还有一个细节,注意到一个DAG的对偶图还是一个DAG,且对于任何一条 S S T的路径一定存在一条边 (u,v) ( u , v ) 使得 Su S → u 上的点在 S S 割,vT上的点全在 T T 割(就是不会S,T间隔,这样才对应原图一个合法路径),那么加上颜色限制后可能破坏这一点,所以在本题对偶图中每个点要向其父亲连一条容量为正无穷的边保证这一点。
代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
#define N 1010
using namespace std;
const int inf=0x3f3f3f3f;
int n,m,c[N],z[N],S,T,dl[N<<1],ne[N<<1],st[N];
vector<int> gt[N],gr[N],tr[N];
bool bt[N],br[N];
struct node
{
    int x,y,w;
}e[N];
struct edge
{
    int t,c,f;
    edge *next,*rev;
}*con[N<<1];
void ins(int x,int y,int c)
{
    edge *p=new edge;p->t=y;p->c=c;p->f=0;p->next=con[x];con[x]=p;
    p=new edge;p->t=x;p->c=0;p->f=0;p->next=con[y];con[y]=p;
    con[x]->rev=con[y];con[y]->rev=con[x];
}
bool cmp(node p,node q)
{
    if(p.y-p.x==q.y-q.x) return p.x<q.x;
    return (p.y-p.x<q.y-q.x);   
}
bool bfs()
{
    memset(ne,0,sizeof(ne));
    ne[S]=1;dl[1]=S;bool re=0;
    for(int hd=1,tl=1,v=S;hd<=tl;re|=(v==T),v=dl[++hd])
        for(edge *p=con[v];p;p=p->next)
            if(p->c>p->f&&ne[p->t]==0) ne[p->t]=ne[v]+1,dl[++tl]=p->t;
    return re;
}
int dinic(int v,int flow)
{
    if(v==T) return flow;
    if(ne[v]==-1) return 0;
    int re=0;
    for(edge *p=con[v];p&&flow;p=p->next)
        if(p->c>p->f&&ne[p->t]==ne[v]+1)
        {
            int o=dinic(p->t,min(flow,p->c-p->f));
            p->f+=o;re+=o;
            p->rev->f-=o;flow-=o;
        }
    if(!re) ne[v]==-1;
    return re;  
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
        {scanf("%d",&c[i]);z[i]=c[i];}
    for(int i=1;i<=m;i++)
    {   
        scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
        gt[e[i].x].push_back(e[i].y);
        gr[e[i].y].push_back(e[i].x);
    }
    bt[0]=1;
    for(int i=0;i<=n;i++)
        if(bt[i])
            for(int j=0;j<gt[i].size();j++)
                bt[gt[i][j]]=1;
    br[n]=1;
    for(int i=n;i>=0;i--)
        if(br[i])
            for(int j=0;j<gr[i].size();j++)
                br[gr[i][j]]=1;
    if(!bt[n])
    {
        puts("-1");
        return 0;
    }
    int mm=m;m=0;
    for(int i=1;i<=mm;i++)
        if(bt[e[i].x]&&bt[e[i].y]&&br[e[i].x]&&br[e[i].y]) e[++m]=e[i];
    sort(e+1,e+m+1,cmp);
    S=0;T=n+m+1;
    for(int i=1;i<=m;i++)
    {
        int v=S;
        for(int j=i+1;j<=m;j++)
            if(v||(e[j].x<=e[i].x&&e[j].y>=e[i].y)) {v=j;break;}
        tr[v].push_back(i);
    }
    for(int i=0;i<=m;i++)
        for(int j=0;j<tr[i].size();j++)
            ins(i,tr[i][j],e[tr[i][j]].w),ins(tr[i][j],i,inf);
    for(int i=0;i<=m;i++)
    {
        int top=0;
        for(int j=0;j<tr[i].size();j++)
            st[++top]=e[tr[i][j]].x;
        sort(st+1,st+top+1);
        for(int j=2;j<=top;j++) 
            ins(i,c[st[j]]+m,inf),ins(c[st[j]]+m,i,inf);
    }
    for(int i=1;i<=m;i++)
        if(!tr[i].size())
        {
            ins(i,T,inf);
            for(int j=e[i].x+1;j<e[i].y;j++)
                ins(i,c[j]+m,inf),ins(c[j]+m,i,inf);
        }
    int ans=0;
    while(bfs()) ans+=dinic(S,inf);
    if(ans<inf) printf("%d",ans);
    else puts("-1");    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值