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

keep_of_silence
菜就多练练||现在使用小号call_of_silence搬运于
2025-08-24 22:51:00,当前版本为作者最后更新于2023-10-07 17:47:29,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
思路
题目给出了每个点的初度和入度,由于图是无重边无自环的有向图。
要求满足限制的图有多少条边与建图方案。
我们可以考虑使用网络流中的最大流。
我们知道这是一道网络流后,题目的难点就转移到网络流的建模以及输出方案的办法。
网络流的建模:
题目所给的条件没有源点汇点并指出图可以不连通,所以考虑设立一个超级源点 以及汇点 。
由于有入度和初度的要求,图是有向图。
考虑拆点。
将每一个点拆成两个点,一个代表由其他点连向自己,即入度。
反之同理,另一个点代表自己连向其他点,即出度。
针对由有些边不能连,由于本题数据较小,我们可以使用一个邻接矩阵来存该边可不可以连。
注意:没有自环,所以自己不能连自己。
所以综上所述,本题的建模规则:
1:每个点的入度连向汇点,流量为入度 。
2:源点连向每个点的出度,流量为出度 。
3:可以连的边,起点的出度点连向终点的入读点,流量为 1(没有重边)。
保证有解,所以建模规则 1 和 2 中的连的边满流。
然后就可以跑一遍最大流 Dinic 板子。
输出方案:
连的边必然是建模规则 3 中连的边,当该边被选中时必然满流(因为流量只有 1)。
但这种边一定是由出度点连向入读点。
下面给出代码:
代码
#include<iostream> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define inf 1e9 using namespace std; int n,m,s,t; int mapp[1005][1005]; int cnt=-1; struct N{ int from,to,next,val; };N p[10000005]; int head[8005],cur[8005],d[8005]; inline void add(int a,int b,int c) { ++cnt; p[cnt].from=a; p[cnt].next=head[a]; head[a]=cnt; p[cnt].to=b; p[cnt].val=c; return ; } inline int bfs() { queue<int> q; q.push(s); memset(d,-1,sizeof(d)); d[s]=0; while(q.size()>0) { int u=q.front(); q.pop(); for(int i=head[u];i!=-1;i=p[i].next) { int v=p[i].to; if(d[v]==-1&&p[i].val>0) { d[v]=d[u]+1; q.push(v); } } } if(d[t]==-1) return 0; else return 1; } inline int dfs(int u,int limit) { if(u==t||!limit) return limit; int sum=0,flow=0; for(int i=cur[u];i!=-1;i=p[i].next) { int v=p[i].to; cur[u]=i; if(d[v]==d[u]+1&&p[i].val>0) { sum=dfs(v,min(p[i].val,limit)); p[i].val-=sum; p[i^1].val+=sum; flow+=sum; limit-=sum; if(!limit) break; } } if(!flow) d[u]=-1; return flow; } inline void dinic() { int ans=0; while(bfs()) { for(int i=0;i<=t;i++) cur[i]=head[i]; ans+=dfs(s,inf); } cout<<ans<<endl; return ; }//板子 int main() { ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); memset(head,-1,sizeof(head));//s=0,memset head数组为-1 cin>>n; s=0,t=2*n+1; int a,b; for(int i=1;i<=n;i++) { cin>>a; add(i+n,t,a),add(t,i+n,0);//建模规则1 } for(int i=1;i<=n;i++) { cin>>a; add(s,i,a),add(i,s,0);//建模规则2 } //拆点:这里我们将出度点的编号设为1-n,入度点就是n+1-2*n,所以汇点t可设为2*n+1 cin>>m; for(int i=1;i<=m;i++) { cin>>a>>b; mapp[a][b]=1;//邻接矩阵 } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(mapp[i][j]==0&&i!=j)//当此边没有被限制且不是自己连向自己时 { add(i,n+j,1),add(n+j,i,0);//连边 } } } dinic(); for(int i=4*n-1;i<=cnt;i++)//建图规则3中连的边cnt值从4*n-1到cnt { if(i%2==0&&p[i].val==0)//是从出度连向入度的边且满流了(val值为0) { cout<<p[i].from<<" "<<p[i].to-n<<endl;//输出时入度点要记得减去n } } return 0; }
- 1
信息
- ID
- 7817
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者