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

Siyuan
Dream OIer.搬运于
2025-08-24 22:02:24,当前版本为作者最后更新于2018-12-17 19:14:56,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题目链接:Luogu 4662
Byteland 国警方收到了一条匿名举报,其中说当地黑帮老大正计划一次从港口到郊区仓库的运输。警方知道运输的时间并且知道运输需要用到国家的高速公路网。
高速公路网包含 个收费站和 条双向的高速公路,每个路段直接连着两个不同的收费站。一个收费站可能与很多其他的收费站相连。汽车只能通过收费站进入或离开高速公路网。据所知,黑帮会距港口边最近的收费站进入高速公路,从距仓库最近的收费站离开(不会在出高速后重新进入)。特警组位于选定的收费站处。当运输车辆进入被监控的收费站时,它就会被警察抓住。
从这个角度看,最简单的办法就是在每个收费站处都安排特警班。然而,控制第 个收费站需要特定的费用 ,每个收费站费用不同。警方想要让花费最小,所以他们需要制定一个收费站的最小控制集,这个集合满足两个条件:
- 所有从港口到仓库的交通必须至少经过集合中的一个收费站。
- 监控这些收费站的费用(即监控每一个收费站费用之和)最小。
- 你可以假设使用高速公路可以从港口到仓库。
你需要找到收费站的最小控制集。
数据范围:,,
Solution
我们需要将一些收费站安排特警班,使得源点和汇点不连通,于是这个问题就是最小割问题。
我们考虑如何将割点转化成割边。由于每个点只能被割一次,我们首先需要拆点:将点 拆成 和 两个点,其中 和 之间连容量为代价(本题中代价为 )的边。对于对于一条边 ,也就转化为 和 ,其中容量为 使得这条边不可能被割掉,即割掉的边一定为 这样的边。
由于源点和汇点也可以被割,所以源点转化为 ,汇点转化为 。
最后我们证明一下正确性。此时,整张图的形态没有改变,每个点只能被割一次对应着每条边 只能被割一次,正确性得证。
对于控制集,我们从源点 出发沿着残量大于 的边开始 ,将所有访问的点打标记。对于一条正向边 ,如果 被打了标记而 没有被打标记,那么就意味着这条边所代表的点被割掉了。
时间复杂度:
Code
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <vector> const int N=4e2+5,M=1e5+5; const int INF=1<<30; int n,m,tot=1,lnk[N],ter[M],nxt[M],val[M],dep[N],cnr[N]; bool vis[N]; void add(int u,int v,int w) { ter[++tot]=v,nxt[tot]=lnk[u],lnk[u]=tot,val[tot]=w; } void addedge(int u,int v,int w) { add(u,v,w),add(v,u,0); } int bfs(int s,int t) { memset(dep,0,sizeof(dep)); memcpy(cnr,lnk,sizeof(lnk)); std::queue<int> q; q.push(s),dep[s]=1; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=lnk[u];i;i=nxt[i]) { int v=ter[i]; if(val[i]&&!dep[v]) q.push(v),dep[v]=dep[u]+1; } } return dep[t]; } int dfs(int u,int t,int flow) { if(u==t) return flow; int ans=0; for(int i=cnr[u];i&&ans<flow;i=nxt[i]) { cnr[u]=i; int v=ter[i]; if(val[i]&&dep[v]==dep[u]+1) { int x=dfs(v,t,std::min(val[i],flow-ans)); if(x) val[i]-=x,val[i^1]+=x,ans+=x; } } if(ans<flow) dep[u]=-1; return ans; } void dinic(int s,int t) { while(bfs(s,t)) while(dfs(s,t,INF)); } void dfs(int u) { vis[u]=1; for(int i=lnk[u];i;i=nxt[i]) if(val[i]&&!vis[ter[i]]) dfs(ter[i]); } int main() { int s,t; scanf("%d%d%d%d",&n,&m,&s,&t); for(int i=1;i<=n;++i) { int x; scanf("%d",&x); addedge(i,i+n,x); } while(m--) { int u,v; scanf("%d%d",&u,&v); addedge(u+n,v,INF),addedge(v+n,u,INF); } dinic(s,t+n); dfs(s); std::vector<int> ans; for(int i=2;i<=tot;i+=2) { int u=ter[i^1],v=ter[i]; if(vis[u]&&!vis[v]) ans.push_back(u); } std::sort(ans.begin(),ans.end()); for(int sz=(int)ans.size()-1,i=0;i<=sz;++i) { printf("%d%c",ans[i]," \n"[i==sz]); } return 0; }
- 1
信息
- ID
- 3640
- 时间
- 1000ms
- 内存
- 32MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者