网络流24题 魔术球问题

题目传送门

又是一个神奇的建图题,建图(Van)♂全不会啊

大体就是我们一个一个的把球放进来,每放进来一个,我们就求出当前的最小路径覆盖数(当前顶点数-最大流),直到最小路径覆盖数({>})柱子数。此时的球的编号(-1)就是第一问的答案。第二问就是求每一条路径,顺着推下来就好了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
struct zzz{
	int t,len,nex;
}e[100010<<1]; int head[20010],tot=1;
void add(int x,int y,int z){
	e[++tot].t=y;
	e[tot].len=z;
	e[tot].nex=head[x];
	head[x]=tot;
}
int s=0,t=20000,vis[20010],pre[20010];
bool bfs(){
	memset(vis,0,sizeof(vis));
	queue <int> q; q.push(s); vis[s]=1;
	while(!q.empty()){
		int k=q.front(); q.pop();
		for(int i=head[k];i;i=e[i].nex){
			int to=e[i].t;
			if(vis[to]||!e[i].len) continue;
			q.push(to); vis[to]=vis[k]+1;
			if(to==t) return 1;
		}
	}
	return vis[t];
}
int dfs(int now,int flow){
	if(now==t||!flow) return flow;
	int rest=0,fl;
	for(int i=head[now];i;i=e[i].nex){
		int to=e[i].t;
		if(vis[to]==vis[now]+1&&(fl=dfs(to,min(flow,e[i].len)))){
			e[i].len-=fl, e[i^1].len+=fl, rest+=fl;
			if(rest==flow) return rest;
		}
	}
	if(rest<flow) vis[now]=0;
	return rest;
}
int dinic(){
	int ans=0;
	while(bfs()) ans+=dfs(s,2147483647);
	return ans;
}
int read(){
	int k=0; char c=getchar();
	for(;c<'0'||c>'9';) c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  k=(k<<3)+(k<<1)+c-48;
	return k;
}
bool jl[20010]; int next[20010];
int main(){
	int n=read(),cnt=0,num=0;
	while(cnt<=n){
		num++; cnt++;
		for(int i=1;i<num;i++)
		  if(sqrt(i+num)==(int)sqrt(i+num))
			add(i,num+10000,1),add(num+10000,i,0);
		add(s,num,1); add(num,s,0);
		add(num+10000,t,1); add(t,num+10000,0);
		cnt-=dinic();
	}
	printf("%d
",--num);
	for(int i=1;i<=num;i++){
		for(int j=head[i];j;j=e[j].nex){
			if(!e[j].len){
				next[i]=e[j].t-10000;
				break;
			}
		}
	}
	for(int i=1;i<=num;i++){
		if(jl[i]) continue;
		int x=i;
		while(x!=-10000){
			jl[x]=1; printf("%d ",x);
			x=next[x];
		}
		printf("
");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/morslin/p/11854767.html