1. 程式人生 > 實用技巧 >ZJU-Searching for talent women(最短路+二分圖匹配+二分答案)

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\n"); //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\n",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\n");
            //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\n",u);
    //}
}
View Code