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); //} }
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); //} }
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法
· Vite CVE-2025-30208 安全漏洞