会建图的话基本就是网络流的模板题
思路很简单->_->先建立一个超级源点,然后将这个点上连接每个单位以单位人数为流量的边,由于单位不能重合,就把每个单位向每张桌子连一条容量为1的边,最后跑一遍最大流观察和单位总人数是否相等,而这里求最大流是使用的dinic算法,所以最后走完过后,连向餐桌的每一条边的增广路都是有值的,扫描一遍有值的增广路,然后输出即可
代码实现如下
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int MAXN=1e5+5;
const int INF=1e9+7;
int n,m;
struct Edge
{
int nxt;
int to;
int f;
}edge[MAXN<<1];
int num=1;
int head[MAXN];
int src;
int sink;
void add(int from,int to,int f)
{
edge[++num].nxt=head[from];
edge[num].to=to;
edge[num].f=f;
head[from]=num;
edge[++num].nxt=head[to];
edge[num].to=from;
edge[num].f=0;
head[to]=num;
}
int dist[MAXN];
bool vis[MAXN];
bool bfs()
{
std::queue<int>q;
std::memset(dist,0,sizeof(dist));
std::memset(vis,0,sizeof(vis));
q.push(src);
dist[0]=0;
vis[0]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(!vis[v]&&edge[i].f)
{
dist[v]=dist[u]+1;
q.push(v);
vis[v]=1;
}
}
}
return vis[sink];
}
int maxflow(int x,int delta)
{
if(x==sink)
{
return delta;
}
int res=0;
for(int i=head[x];i&δi=edge[i].nxt)
{
int v=edge[i].to;
if(dist[v]==dist[x]+1&&edge[i].f)
{
int dd=maxflow(v,std::min(edge[i].f,delta));
res+=dd;
delta-=dd;
edge[i].f-=dd;
edge[i^1].f+=dd;
}
}
return res;
}
int ans=0;
void dinic()
{
while(bfs())
{
ans+=maxflow(src,INF);
}
}
int q[MAXN];
void print()
{
for(int i=1;i<=n;i++)
{
q[0]=0;
std::memset(q,0,sizeof(q));
for(int j=head[i];j;j=edge[j].nxt)
{
int v=edge[j].to;
if(v==src) continue;
if(edge[j].f==0)
{
q[++q[0]]=v-n;
}
}
for(int k=q[0];k>0;k--)
{
printf("%d ",q[k]);
}
printf("\n");
}
}
int main()
{
int sum=0;
std::scanf("%d%d",&n,&m);
sink=n+m+1;//惨痛教训,要在输入后定义
src=0;
for(int i=1;i<=n;i++)
{
int x;
std::scanf("%d",&x);
add(src,i,x);
for(int j=n+1;j<=n+m;j++)
{
add(i,j,1);
}
sum+=x;
}
for(int i=n+1;i<=m+n;i++)
{
int x;
std::scanf("%d",&x);
add(i,sink,x);
}
dinic();
if(ans==sum)
{
printf("1\n");
print();
}
else
printf("0\n");
return 0;
}