[FJOJ190]网络流+二分答案

FZOJ190

题目描述

  • 神仙题,自闭场不要在意。了解了算法的思路,来写一下博客。(也就当是练一下板子233)
  • 做这道题大概是这样一个思路过程:
    • 是在1~n的所有路径上,选择不超过k个点,点的权值变成1,使得最短路最大,求最大值。
    • 一般情况下,使最小值最大或者使最大值最小这种问题都是先二分答案。
    • 我们二分一个mid作为答案,那么从1到n一共走了mid步。考虑怎么判断是否有可行解。
    • 首先应该考虑的是如何把点权转化为边的关系,然后想到拆点。从x0到x1连一条权值为1的边,代表直接经过了x这个点。拆点一般什么时候用啊?网络流(不要问我什么神仙想法,我也不知道啊)。所以应该建分层图跑最小割判断是否有可行解。
    • 为了使每个点的权值只能贡献一次,我们只把当前层的x0与下一层的x1连一条容量为inf的边。
    • 对于一条边x,y,把每个当前层的x1向下一层的y0连一条容量为inf的边。当然,每一层的n0也要向下一层的n0连一条容量inf的边。
    • 求第1层的s1到第mid-1层的n0的最小割。最小割意味着什么?画一下图,它代表的含义就是使用最少的点,让s1无法到达第mid-1层的n0(无法到达mid-1的n0代表着它一定到达了mid-1层之后的分层图,答案>=mid,符合条件)。
    • 哎,这题太神仙了。

Coding

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=1e5+10;
const int inf=1e6;
struct node{int x,y;}e[N];long long maxflow,flow;
int n,m,k,s,t,tot,ver[N*50],Next[N*50],lin[N*50],edge[N*50],d[N*50];
int cal(int i,int j,int temp){return (i-1)*2*n+temp*n+j;}
void add(int x,int y,int z){
	ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;edge[tot]=z;
	ver[++tot]=x;Next[tot]=lin[y];lin[y]=tot;edge[tot]=0;
}
bool bfs(){
	memset(d,0,sizeof(d));
	queue<int>q;
	q.push(s);d[s]=1;
	while(q.size()){
		int x=q.front();q.pop();
		for(int i=lin[x];i;i=Next[i]){
			if(edge[i]){
				int y=ver[i];
				if(!d[y]){
					d[y]=d[x]+1;
					q.push(y);
					if(y==t) return 1;
				}
			}
		}
	}
	return 0;
}
int dinic(int x,int flow){
	if(x==t) return flow;
	int rest=flow,num;
	for(int i=lin[x];i&&rest;i=Next[i]){
		int y=ver[i];
		if(edge[i]&&d[y]==d[x]+1){
			num=dinic(y,min(edge[i],rest));
			if(!num) d[y]=0;
			rest-=num;edge[i]-=num;edge[i^1]+=num;
			if(!rest) return flow-rest;
		}
	}
	return flow-rest;
}
bool check(int mid){
	memset(lin,0,sizeof(lin));
	memset(Next,0,sizeof(Next));
	tot=1;
	for(int i=1;i<=mid+1;++i){
		for(int j=2;j<n;++j) 
			add(cal(i,j,0),cal(i,j,1),1);
		if(i!=mid+1){
			for(int j=2;j<n;++j) 
				add(cal(i,j,0),cal(i+1,j,1),inf);
			add(cal(i,n,0),cal(i+1,n,0),inf);
			for(int j=1;j<=m;++j){
				add(cal(i,e[j].x,1),cal(i+1,e[j].y,0),inf);
				add(cal(i,e[j].y,1),cal(i+1,e[j].x,0),inf);
			}
		}
	}maxflow=0;
	s=cal(1,1,1);t=cal(mid+1,n,0);
	while(bfs()){
		while(flow=dinic(s,inf)) 
			maxflow+=flow;
		if(maxflow>k) return 0;
	}
	return maxflow<=k;
}
int main(){
	freopen("min.in","r",stdin);
	freopen("min.out","w",stdout);
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;++i){
		scanf("%d%d",&e[i].x,&e[i].y);
		e[i].x++,e[i].y++;
	}
	int l=0,r=2*n;
	while(l+1<r){
		int mid=l+r>>1;
		if(check(mid)) l=mid;
		else r=mid;
	}
	if(check(l)) printf("%d\n",r);
	else printf("%d\n",l);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39759315/article/details/88553126