zoukankan      html  css  js  c++  java
  • 【JZOJ3348】秘密任务【网络流】

    题目大意:

    题目链接:https://jzoj.net/senior/#main/show/3348
    在这里插入图片描述


    思路:

    这道题的提示还是很明显的,几乎一眼就可以看出是网络流。
    首先,我们要知道从点1到点nn有哪些最短路,因为只有最短路才会对最终答案有贡献,换句话说,若你在一条非最短路上的边建立检查点其实是没有任何用处的。
    所以我们从点1和点nn都跑一边最短路,然后枚举每一条边,这条边的左右端点设为x,yx,y。若满足dis[1][x]+e[i].dis+dis[y][n]=dis[1][n]dis[1][x]+e[i].dis+dis[y][n]=dis[1][n],那么说明这条边一定是最短路上的边。于是就把这条边加入网络中。
    我们发现,将一条边把守的代价其实就是在两端端点设立检查点的代价之一。所以我们把网络中的每一条边分割,在其中插入一个新的节点,这个节点左右两边的流量分别为两个端点设立检查点的代价。然后跑一边最大流,就是第二问的答案。
    那么如何判断最优方案是否唯一呢?我们发现,如果所有的必割边的流量之和等于最大流,那么割掉这些边之后就没有其他边可以选择了。此时的割法就唯一。
    如何判断网络中的某一条边是否为必割边呢?我们只要利用网络的残余流量来跑,看看每一条边所连接的两个点是否是分别属于源点集合和汇点集合的点即可。如果是,那么这条边就是必割边,因为如果不割这条边那么就依然可以有流量经过这条边,就不是“最小割”了。


    代码:

    #include <queue>
    #include <string>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const ll N=1010,M=20010,Inf=1e9;
    ll head[N],dis[3][N],dep[N],cur[N],X[M],Y[M],D[M],a[N];
    ll n,m,Q,S,T,tot,num,sum,maxflow;
    ll vis[N];
    
    struct edge
    {
        ll next,to,dis,flow,from;
    }e[M];
    
    ll read()
    {
        ll d=0;
        char ch=getchar();
        while (!isdigit(ch)) ch=getchar();
        while (isdigit(ch))
            d=(d<<3)+(d<<1)+ch-48,ch=getchar();
        return d;
    }
    
    void add(ll from,ll to,ll dis,ll flow)
    {
        e[++tot].to=to;
        e[tot].dis=dis;
        e[tot].from=from;
        e[tot].flow=flow;
        e[tot].next=head[from];
        head[from]=tot;
    }
    
    void spfa(ll S,ll id)
    {
        memset(dis[id],0x3f3f3f3f,sizeof(dis[id]));
        memset(vis,0,sizeof(vis));
        queue<int> q;
        q.push(S);
        dis[id][S]=0;
        while (q.size())
        {
            ll u=q.front(),v;
            q.pop();
            vis[u]=0;
            for (ll i=head[u];~i;i=e[i].next)
            {
                v=e[i].to;
                if (dis[id][v]>dis[id][u]+e[i].dis)
                {
                    dis[id][v]=dis[id][u]+e[i].dis;
                    if (!vis[v])
                    {
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
    }
    
    bool bfs()
    {
        memcpy(cur,head,sizeof(cur));
        memset(dep,0x3f3f3f3f,sizeof(dep));
        queue<int> q;
        q.push(S);
        dep[S]=1;
        while (q.size())
        {
            ll u=q.front(),v;
            q.pop();
            for (ll i=head[u];~i;i=e[i].next)
            {
                v=e[i].to;
                if (dep[v]>dep[u]+1&&e[i].flow)
                {
                    dep[v]=dep[u]+1;
                    q.push(v);
                }
            }
        }
        return dep[T]<Inf;
    }
    
    ll dfs(ll x,ll flow)
    {
        ll low=0;
        if (x==T)
        {
            maxflow+=flow;
            return flow;
        }
        ll used=0;
        for (ll i=cur[x];~i;i=e[i].next)
        {
            ll y=e[i].to;
            cur[x]=i;
            if (dep[y]==dep[x]+1&&e[i].flow)
            {
                low=dfs(y,min(e[i].flow,flow-used));
                if (low)
                {
                    used+=low;
                    e[i].flow-=low;
                    e[i^1].flow+=low;
                    if (used==flow) break;
                }
            }
        }
        return used;
    }
    
    void dinic()
    {
        while (bfs())
            dfs(S,Inf);
    }
    
    void dfs1(ll x,ll id)
    {
        vis[x]=id;
        for (ll i=head[x];~i;i=e[i].next)
        {
            ll v=e[i].to;
            if (!vis[v] && id==1 && e[i].flow) dfs1(v,id);
            if (!vis[v] && id==2 && e[i^1].flow) dfs1(v,id);
        }
    }
    
    int main()
    {
        Q=read();
        while (Q--)
        {
            n=read(); m=read();
            for (ll i=1;i<n;i++) a[i]=read();
            a[n]=Inf;
            memset(head,-1,sizeof(head));
            tot=0;
            for (ll i=1;i<=m;i++)
            {
                X[i]=read(); Y[i]=read(); D[i]=read();
                add(X[i],Y[i],D[i],0);
                add(Y[i],X[i],D[i],0);
            }
            spfa(1,1); spfa(n,2);
            memset(head,-1,sizeof(head));
            tot=1; num=n; S=1; T=n; maxflow=0;
            for (ll i=1;i<=m;i++)
                if (dis[1][X[i]]+D[i]+dis[2][Y[i]]==dis[1][T])
                {
                    num++;
                    add(X[i],num,0,a[X[i]]);
                    add(num,X[i],0,0);
                    add(num,Y[i],0,a[Y[i]]);
                    add(Y[i],num,0,0);
                }
                else if (dis[2][X[i]]+D[i]+dis[1][Y[i]]==dis[1][T])
                {
                	num++;
                    add(Y[i],num,0,a[Y[i]]);
                    add(num,Y[i],0,0);
                    add(num,X[i],0,a[X[i]]);
                    add(X[i],num,0,0);
    			}
            dinic();
            memset(vis,0,sizeof(vis));
            dfs1(S,1); dfs1(T,2);
            sum=0;
            for (ll i=2;i<=tot;i++)
                if (vis[e[i].from] && vis[e[i].to] && vis[e[i].from]!=vis[e[i].to])
                    sum+=e[i].flow;
            if (sum==maxflow) printf("Yes %lld
    ",maxflow);
                else printf("No %lld
    ",maxflow);
        }
        return 0;
    }
    
  • 相关阅读:
    数组
    循环(二)
    循环——笔记
    C#基础(四)
    C#基础(三)
    安卓帮助文档
    增加线程异步发送消息的方法二(Runnable)
    增加线程异步发送消息的方法一(Thread)
    获取下拉框的值
    获取表中字段最大值,并且保存在前台页面中
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998091.html
Copyright © 2011-2022 走看看