洛谷 P3254 圆桌问题 题解
这题是网络流24题中难度较小的一道题,并没有什么思维难点
-
从源点向
m
个单位分别连边,流量为其人数 -
分别从
n
张桌子向汇点连边,流量为其容量 -
从
m
张桌子分别向n
张桌子连边,流量为1
(因为同一个单位来的代表不在同一个餐桌就餐。)
若最大流等于总人数,则有解,否则无解。
路径输出也不难,统计所有以单位为起点的边,若终点不为源点且为满流,就输出该边的终点。
附代码:
PS:为了使得输出和样例顺序一致,代码中使用了stack
以逆序输出。
#ifndef ONLINE_JUDGE
#define _EXT_ENC_FILEBUF_H 1
#define _EXT_CODECVT_SPECIALIZATIONS_H 1
#endif // ONLINE_JUDGE
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
constexpr int Size=1e6+1,S=0,T=Size-2,Inf=INT_MAX;
struct edge{
int v,w,nxt;
}e[Size];
int n,m,cnt=1;
long long sum;
int cur[Size],dep[Size],head[Size];
stack<int> ans;
int Dinic();
bool bfs();
int dfs(int u,int flow=Inf);
void add_edge(int u,int v,int w,bool flag=true);
inline int tbl(int pos){return pos+n;};
int main()
{
scanf("%d%d",&n,&m);
for(int i=1,flow;i<=n;i++){
scanf("%d",&flow);
sum+=flow;
add_edge(S,i,flow);
}
for(int i=1,flow;i<=m;i++){
scanf("%d",&flow);
for(int j=1;j<=n;j++)
add_edge(j,tbl(i),1);
add_edge(tbl(i),T,flow);
}
if(Dinic()!=sum){
putchar('0');
exit(EXIT_SUCCESS);
}
puts("1");
for(int i=1;i<=n;i++){
for(int j=head[i];j;j=e[j].nxt)
if(e[j].v!=S && e[j].w==0)
ans.push(e[j].v-n);
while(!ans.empty()){
printf("%d ",ans.top());
ans.pop();
}
putchar('\n');
}
}
int Dinic()
{
int ret=0;
while(bfs()){
memcpy(cur,head,sizeof(head));
int d=dfs(S);
while(d!=0){
ret+=d;
d=dfs(S);
}
}
return ret;
}
int dfs(int u,int flow)
{
if(u==T)
return flow;
for(int &i=cur[u];i;i=e[i].nxt)
if(dep[e[i].v]==dep[u]+1&&e[i].w!=0){
int di=dfs(e[i].v,min(flow,e[i].w));
if(di<=0) continue;
e[i].w-=di;
e[i^1].w+=di;
return di;
}
return 0;
}
bool bfs()
{
queue<int> q;
memset(dep,0,sizeof(dep));
dep[S]=1;
q.push(S);
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=head[now];i;i=e[i].nxt)
if(dep[e[i].v]==0&&e[i].w>0){
dep[e[i].v]=dep[now]+1;
q.push(e[i].v);
}
}
return dep[T]>0;
}
void add_edge(int u,int v,int w,bool flag)
{
cnt++;
e[cnt].v=v;
e[cnt].w=w;
e[cnt].nxt=head[u];
head[u]=cnt;
if(flag) add_edge(v,u,0,false);
}