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
原文地址:https://www.cnblogs.com/zhanglichen/p/13512807.html