zoukankan      html  css  js  c++  java
  • ZJU-Searching for talent women(最短路+二分图匹配+二分答案)

    题意:

    给出一个无向图,由v个点和m条边组成。

    给出n个初始位置,这些位置可能重复。请你为k个位置确定一个终点,使得这k个位置到各自终点的最大距离最小。

    题解:

    比赛的时候写了四个小时最小费用最大流,现在才知道费用流是处理不了这种单个路线费用最大的问题的。

    首先对所有初始位置处理出他们到所有点的最短路。

    然后考虑二分答案,对于当前二分值Mid,在连边时只连距离小于等于Mid的边,然后跑二分图匹配。这里出题人还是比较友好的,没有卡Dinic,用HK也可以。

    Dinic版本:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+100;
    const int inf=1e9;
    typedef long long ll;
    struct node {
        int u,v,w,nxt;
    }edge[maxn*2];
    int head[maxn];
    int tot;
    int v,m,n,k;
    int a[maxn];//初始位置 
    void addedge (int u,int v,int w) {
        edge[tot].u=u;
        edge[tot].v=v;
        edge[tot].w=w;
        edge[tot].nxt=head[u];
        head[u]=tot++;
        
        edge[tot].u=v;
        edge[tot].v=u;
        edge[tot].w=w;
        edge[tot].nxt=head[v];
        head[v]=tot++;
    }
     
    int d[605][605];//每个儿子和所有人的最短距离
    int vis[maxn];
    struct qnode {
        int v,w;
        bool operator < (const qnode &r) const {
            return w>r.w;
        }
    };
    void dij (int s) {
        for (int i=1;i<=v;i++) d[s][i]=inf,vis[i]=0;
        d[s][a[s]]=0;
        priority_queue<qnode> q;
        q.push({a[s],d[s][a[s]]});
        while (!q.empty()) {
            qnode tt=q.top();
            q.pop();
            int u=tt.v;
            vis[u]=1;
            for (int i=head[u];i!=-1;i=edge[i].nxt) {
                int v=edge[i].v;
                if (!vis[v]&&d[s][u]+edge[i].w<d[s][v]) {
                    d[s][v]=d[s][u]+edge[i].w;
                    q.push({v,d[s][v]});
                }
            }
        }
    } 
    
    void addedge1 (int u,int v,int w) {
        edge[tot].u=u;
        edge[tot].v=v;
        edge[tot].w=w;
        edge[tot].nxt=head[u];
        head[u]=tot++;
        
        edge[tot].u=v;
        edge[tot].v=u;
        edge[tot].w=0;
        edge[tot].nxt=head[v];
        head[v]=tot++;
    } 
    int dep[maxn];
    int inque[maxn];
    int vi;
    int cur[maxn];
    int maxflow=0;
    int s,t;
    bool bfs () {
        for (int i=0;i<=t;i++) 
            cur[i]=head[i],dep[i]=inf,inque[i]=0;
        dep[s]=0;
        queue<int> q;
        q.push(s);
        while (!q.empty()) {
            int u=q.front();
            q.pop();
            inque[u]=0;
            for (int i=head[u];i!=-1;i=edge[i].nxt) {
                int v=edge[i].v;
                if (dep[v]>dep[u]+1&&edge[i].w) {
                    dep[v]=dep[u]+1;
                    if (inque[v]==0) {
                        q.push(v);
                        inque[v]=1;
                    } 
                }
            }
        }
        if (dep[t]!=inf) return 1;
        return 0;
    }
    int dfs (int u,int flow) {
        int increase=0;
        if (u==t) {
            vi=1;
            maxflow+=flow;
            return flow;
        }
        int used=0;
        for (int i=cur[u];i!=-1;i=edge[i].nxt) {
            cur[u]=i;
            int v=edge[i].v;
            if (edge[i].w&&dep[v]==dep[u]+1) {
                if (increase=dfs(v,min(flow-used,edge[i].w))) {
                    used+=increase;
                    edge[i].w-=increase;
                    edge[i^1].w+=increase;
                    if (used==flow) break;
                }
            }
        }
        return used;
    }
    int Dinic () {
        maxflow=0;
        while (bfs()) {
            vi=1;
            while (vi==1) {
                vi=0;
                dfs(s,inf);
            }
        } 
        return maxflow;
    }
    
    
    int main () {
        //while (~scanf("%d%d%d%d",&v,&m,&n,&k)) {
            scanf("%d%d%d%d",&v,&m,&n,&k);
            for (int i=1;i<=v;i++) head[i]=-1;
            tot=0;
            set<int> st;
            for (int i=1;i<=n;i++) {
                scanf("%d",a+i);
            }
            if (st.size()>=k) {
                printf("0
    ");
                //continue;
                return 0;
            } 
            for (int i=1;i<=m;i++) {
                int u,v,w;
                scanf("%d%d%d",&u,&v,&w);
                addedge(u,v,w);
            }
            for (int i=1;i<=n;i++) dij(i);
            int Max=0;
            for (int i=1;i<=n;i++)
                for (int j=1;j<=v;j++)
                    if (d[i][j]!=inf)Max=max(Max,d[i][j]);
            int l=1,r=Max,u=-1;
            while (l<=r) {
                int mid=(l+r)>>1;
                for (int i=0;i<=n+v+1;i++) head[i]=-1;
                tot=0;
                s=0,t=n+v+1;
                for (int i=1;i<=n;i++) {
                    addedge1(s,i,1);
                    for (int j=1;j<=v;j++) {
                        if (d[i][j]<=mid)
                            addedge1(i,j+n,1);
                    }
                }
                for (int i=1;i<=v;i++)    addedge1(n+i,t,1);
                int ans=Dinic();
                if (ans>=k)
                    u=mid,r=mid-1;
                else
                    l=mid+1;
            }
            printf("%d
    ",u);
        //}
    }
    View Code

    HK版本:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+100;
    const int inf=1e9;
    typedef long long ll;
    struct node {
        int u,v,w,nxt;
    }edge[maxn*2];
    int head[maxn];
    int tot;
    int v,m,n,k;
    int a[maxn];//初始位置 
    void addedge (int u,int v,int w) {
        edge[tot].u=u;
        edge[tot].v=v;
        edge[tot].w=w;
        edge[tot].nxt=head[u];
        head[u]=tot++;
        
        edge[tot].u=v;
        edge[tot].v=u;
        edge[tot].w=w;
        edge[tot].nxt=head[v];
        head[v]=tot++;
    }
     
    int d[205][maxn];//每个儿子和所有人的最短距离
    int vis[maxn];
    struct qnode {
        int v,w;
        bool operator < (const qnode &r) const {
            return w>r.w;
        }
    };
    void dij (int s) {
        for (int i=1;i<=v;i++) d[s][i]=inf,vis[i]=0;
        d[s][a[s]]=0;
        priority_queue<qnode> q;
        q.push({a[s],d[s][a[s]]});
        while (!q.empty()) {
            qnode tt=q.top();
            q.pop();
            int u=tt.v;
            vis[u]=1;
            for (int i=head[u];i!=-1;i=edge[i].nxt) {
                int v=edge[i].v;
                if (!vis[v]&&d[s][u]+edge[i].w<d[s][v]) {
                    d[s][v]=d[s][u]+edge[i].w;
                    q.push({v,d[s][v]});
                }
            }
        }
    } 
     
     
    //HK算法
    int dep[maxn];
    int con[maxn];
    bool bfs () {
        for (int i=1;i<=n+v;i++) dep[i]=0;
        queue<int> q;
         for (int i=1;i<=n;i++) if (con[i]==-1) q.push(i);
        int f=0;
        while (!q.empty()) {
            int u=q.front();
            q.pop();
            for (int i=head[u];i!=-1;i=edge[i].nxt) {
                int v=edge[i].v;
                if (!dep[v]) {
                    dep[v]=dep[u]+1;
                    if (con[v]==-1)
                        f=true;
                    else
                        dep[con[v]]=dep[v]+1,q.push(con[v]);
                }
            }
        }
        return f;
    }
    bool dfs (int u) {
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            int v=edge[i].v;
            if (dep[v]!=dep[u]+1) continue;
            dep[v]=0;
            if (con[v]==-1||dfs(con[v])) {
                con[u]=v;
                con[v]=u;
                return true;
            }
        }
        return false;
    }
    int wjm=0;
    int hk () {
        int ans=0;
        while (bfs())
            for (int i=1;i<=n;i++) if (con[i]==-1&&dfs(i)) ans++;
        return ans;
    }
     
     
    int main () {
        //while (~scanf("%d%d%d%d",&v,&m,&n,&k)) {
        scanf("%d%d%d%d",&v,&m,&n,&k);
            for (int i=1;i<=v;i++) head[i]=-1;
            tot=0;
            set<int> st;
            for (int i=1;i<=n;i++) {
                scanf("%d",a+i);
                st.insert(a[i]);
            }
            if (st.size()>=k) {
                printf("0
    ");
                //continue;
                return 0;
            } 
            for (int i=1;i<=m;i++) {
                int u,v,w;
                scanf("%d%d%d",&u,&v,&w);
                addedge(u,v,w);
            }
            for (int i=1;i<=n;i++) dij(i);
            int Max=0;
            for (int i=1;i<=n;i++)
                for (int j=1;j<=v;j++)
                    if (d[i][j]!=inf)Max=max(Max,d[i][j]);
            //二分图匹配
            //二分一个时间
            //每个人和每个点
            //如果d[s][j]小于等于mid,就连一条边
            //跑hk,如果最大匹配大于k,就把mid向下调 
            //否则向下调
            int l=1,r=Max,u=-1;
            while (l<=r) {
                int mid=(l+r)>>1;
                for (int i=0;i<=n+v+1;i++) head[i]=-1;
                for (int i=0;i<=n+v+1;i++) con[i]=-1,dep[i]=0;
                tot=0; 
                for (int i=1;i<=n;i++) {
                    for (int j=1;j<=v;j++) {
                        if (d[i][j]<=mid)
                            addedge(i,j+n,1);
                    }
                }
                int ans=hk();
                if (ans>=k) {
                    r=mid-1;u=mid;
                }
                else
                    l=mid+1;
            }
            printf("%d
    ",u);
        //}
    }
    View Code
  • 相关阅读:
    fedora安装后的配置
    linux下的Shell编程(7)使用-x和-n调试shell程序
    linux下的Shell编程(8)自定义函数
    linux下的Shell编程(6)case和select
    linux下的Shell编程(5)循环
    linux下的Shell编程(4)特殊的变量和占位符
    linux下的Shell编程(3)shell里的流程控制
    Linux下的Shell编程(2)环境变量和局部变量
    Linux下的Shell编程(1)最简单的例子
    Docker学习笔记
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/13512807.html
Copyright © 2011-2022 走看看