题目描述
- 神仙题,自闭场不要在意。了解了算法的思路,来写一下博客。(也就当是练一下板子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;
}