1 条题解
-
0
自动搬运
来自洛谷,原作者为

忘怀星
快关注我好吗?搬运于
2025-08-24 21:51:14,当前版本为作者最后更新于2021-05-21 15:46:16,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
简述题意
给定一些货源地,一些中转岛,以及一个终点——军事基地。
货源地分为两种,普通货和特殊货。
对于中转岛 ,最多可以中转 件货物,中转岛发向另一个中转岛或军事基地的货物分别不能超过 。
上面货物皆指普通货物,特殊货物不会受到限制。
中转岛之间有 条航道,航道 有边权。开辟航线 的代价是 和 之间的最短路。
最后全局的代价是航线代价的最大值。
要求在所有货物都能够到达军事基地的前提下,最小代价是多少。
解题思路
看到代价是最大值,要求其最小,考虑可以二分。将代价小于等于二分答案 的航线全都加进去,看是否能将所有货物都送达军事基地。二分正确性显然。
分别考虑特殊货物和普通货物。
首先考虑特殊货物,只要货源地和军事基地相连,即可全部运往军事基地。我们将所有航线全部连上,并连反边(基地连向中转岛,中转岛连向货源地)。中转岛间边权设为航线开辟代价。从军事基地开始使用最短路算法,最小化到各个货源地的路径上的最大边权。可以发现将所有特殊货源地的答案取 是二分下界。
至于如何求出两岛间航线代价,使用 的最短路即可。
然后考虑如何求是否所有普通货物都可到达军事基地。
使用网络流求最大流。货源地连中转岛容量为 ,中转岛之间和中转岛到军事基地的边容量为 ,将中转岛拆点控制经过岛的流量不超过 。
随后跑最大流就好了,此题解决。
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue> #define int long long #define Mp make_pair #define val lim #define pb push_back using namespace std; int read() { int a = 0,x = 1;char ch = getchar(); while(ch > '9' || ch < '0') {if(ch == '-') x = -1;ch = getchar();} while(ch >= '0' && ch <= '9') {a = a*10 + ch-'0';ch = getchar();} return a*x; } const int N=1e6+7,inf = 1e9+7; int n,m,X,E,a[N],w[N],d[N],b[N],M,s,t; vector<int>g[307]; int mp[500][500],dis[N],vis[N]; int head[N],go[N],nxt[N],cnt,lim[N],cur[N]; void add(int u,int v,int w) { go[++cnt] = v; nxt[cnt] = head[u]; head[u] = cnt; lim[cnt] = w; } struct node{int u,v,w;}edge[N]; void dij(int pos) { priority_queue<pair<int,int> >q; q.push(Mp(0,pos)); while(!q.empty()) { int u = q.top().second; if(vis[u]) {q.pop();continue;} dis[u] = -q.top().first;q.pop();vis[u] = 1; for(int e = head[u];e;e = nxt[e]) { int v = go[e];if(dis[v]) continue; q.push(Mp(min(-dis[u],-lim[e]),v)); } } } bool BFS() { for(int i = s;i <= t;i ++) dis[i] = 0; queue<int>q;q.push(s);dis[s] = 1; while(!q.empty()) { int u = q.front();q.pop(); for(int e = head[u];e;e = nxt[e]) { int v = go[e]; if(dis[v] || !lim[e]) continue; dis[v] = dis[u] + 1;q.push(v); } } return dis[t]; } int DFS(int u,int limit) { if(u == t || !limit) return limit; int ret = 0; for(int &e = head[u];e; e= nxt[e]) { int v = go[e];if(dis[v] != dis[u] + 1|| !lim[e]) continue; int tmp = DFS(v,min(lim[e],limit)); lim[e] -= tmp,lim[e^1] += tmp; limit -= tmp,ret += tmp; if(!limit) break; } return ret; } bool check(int limit) { for(int i = s;i <= t;i ++) head[i] = 0;cnt = 1;int sum = 0; for(int i = 1;i <= n;i ++) add(s,i,a[i]),add(i,s,0),sum += a[i]; //货源地和中转岛 for(int i = 1;i <= m;i ++) { if(b[i]) add(i+n+m,t,d[i]),add(t,i+n+m,0); //中转岛和军事基地 add(i+n,i+n+m,w[i]);add(i+n+m,i+n,0); //拆点部分 for(auto it = g[i].begin();it != g[i].end();++ it) { if(*it <= n) add(*it,i+n,inf),add(i+n,*it,0); //只连普通货源地,特殊货源地不必理会 } } for(int i = 1;i <= M && edge[i].w <= limit;i ++) { add(edge[i].u+n+m,edge[i].v+n,d[edge[i].u]);add(edge[i].v+n,edge[i].u+n+m,0); add(edge[i].v+n+m,edge[i].u+n,d[edge[i].v]);add(edge[i].u+n,edge[i].v+n+m,0); //连上可以连的边 } // puts("!");printf("sum=%d\n",sum); for(int i = s;i <= t;i ++) cur[i] = head[i]; while(BFS()) {sum -= DFS(s,inf);for(int i = s;i <= t;i ++) head[i] = cur[i];} return !sum;//sum=0说明所有货物都可以送到军事基地 } bool cmp(node a,node b) {return a.w < b.w;} signed main() { // freopen("in.in","r",stdin); n = read(),m = read(),X = read(),E = read(); for(int i = 1;i <= n+X;i ++) a[i] = read(); for(int i = 1;i <= m;i ++) w[i] = read(); for(int i = 1;i <= m;i ++) d[i] = read(); for(int i = 1;i <= m;i ++) { int k = read(); for(int j = 1;j <= k;j ++) { int u = read();g[i].pb(u); add(n+X+i,u,0); } b[i] = read();if(b[i]) add(n+X+m+1,n+X+i,0); } for(int i = 1;i <= E;i ++) { int u = read(),v = read(),w = read(); mp[u][v] += w,mp[v][u] += w;//这里是看讨论区提到的,我就这么写了 } for(int i = 1;i <= m;i ++) for(int j = 1;j <= m;j ++) if(!mp[i][j]) mp[i][j] = inf; else if(i!=j) add(n+X+i,n+X+j,mp[i][j]); //边先连上,为处理特殊货物做准备,显然不需要连那些本来没有直接连边的航线。连接他们不会使答案更优。 for(int k = 1;k <= m;k ++) for(int i = 1;i <= m;i ++) for(int j = 1;j <= m;j ++) mp[i][j] = min(mp[i][j],mp[i][k]+mp[k][j]); dij(n+m+X+1);int l = 0,r,mid;//跑最短路 for(int i = n+1;i <= n+X;i ++) l = max(l,dis[i]);//赋值下界 memset(head,0,sizeof(head)); cnt = 1;s = 0,t = n+2*m+1; for(int i = 1;i <= m;i ++) for(int j = i+1;j <= m;j ++) if(mp[i][j] < inf) edge[++M] = (node){i,j,mp[i][j]}; //把所有航线拿出来排序 sort(edge+1,edge+1+M,cmp);r = inf; while(l<r) {//二分答案 mid = l+r>>1; if(check(mid)) r = mid; else l = mid + 1; // printf("mid=%d\n",mid); } if(l != inf) printf("%lld",l); else printf("-1"); }
- 1
信息
- ID
- 2689
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者