前言
搞了几天网络流,总算会做题了
题面
sol
显然,如果是个dp,那么转移就是到某一个点的状态转移给下一个点。
但是下一个点不能被选,也就是一个点被选的状态加上与其有连边的点没有选的状态转移给这一个点被选的状态。
然后发现,这个dp显然很难设出状态与变量意义,但换成网络流很好设。
但是网络流只能求最大流,没有最小。这道题是最小路径覆盖,所以要把最小转换成最大。我们发现,在最小路径覆盖下经过的边是最多的,经过的边是点数减去路径数。所以求最大路径数。
然后约束条件就是一个点和一个边只能经过一次。但是我们发现一个点只能经过一次的约束更强,如果两个点之间有一条边,两点只经过一次,那么他们之间的边经过少于等于一次。
我们发现流过一条边就要算一次贡献,相当于就要往汇点送去一的流量,所以经过一个边就要往汇点送去一的流量。
显然不是每一个边都可以送去流量,如果这边的终点已经经过了,那么就不能送去流量,也就是该终点若经过一次,该边的容量就已经为零,不能再有流量走了,也就不会有往汇点的贡献。
那么这样看来,所有终点相同的边实际上是一条边。我们把这条边叫做上述终点的A边。在网络流构图中,经过这条边就可以去汇点送贡献,这条边的终点是汇点。
由于每一个点都可以作为起点,那么每一个点都是与源点相连。然后每一个点连出一条这个点能去往的一个点的A边的起点的边。
code
#include<bits/stdc++.h>
using namespace std;
template <class T>
inline void read(T&data){
data=0;
register char ch=0;
while(ch<'0'||ch>'9')ch=getchar();
while(ch<='9'&&ch>='0'){
data=(data<<3)+(data<<1)+(ch&15);
ch=getchar();
}
return;
}
const int _ =310,__ = 13001;
const int INF = 2e9;
int n,m,S,T;
int to[__<<1],head[_],cur[_],w[__<<1],nxt[__<<1],deep[_],cnt=-1;
inline void add(register int a,register int b,register int c){
to[++cnt]=b,nxt[cnt]=head[a],w[cnt]=1,head[a]=cnt;
to[++cnt]=a,nxt[cnt]=head[b],w[cnt]=0,head[b]=cnt;
}
inline bool bfs(){
memset(deep,0,sizeof(deep));
deep[S]=1;
queue<int >Q;
Q.push(S);deep[S]=1;
while(!Q.empty()){
register int now=Q.front();Q.pop();
for(register int i=head[now];~i;i=nxt[i])
{
if(deep[to[i]])continue;
if(w[i]<=0)continue;
deep[to[i]]=deep[now]+1;
Q.push(to[i]);
}
}
return deep[T]!=0;
}
int dfs(register int now,register int flow){
if(now==T)return flow;
for(register int &i=cur[now];~i;i=nxt[i]){
if(w[i]<=0)continue;
if(deep[to[i]]!=deep[now]+1)continue;
register int di=dfs(to[i],min(flow,w[i]));
if(di>0){
w[i]-=di;w[i^1]+=di;
return di;
}
}
return 0;
}
inline int dinic(){
register int ret=0;
while(bfs()){
for(register int i=1;i<=T;++i)cur[i]=head[i];
while(int d=dfs(S,INF))ret+=d;
}
return ret;
}
int stk[_];
bool pd[_];
int main(){
read(n),read(m);
memset(head,-1,sizeof(head));
S=2*n+1,T=S+1;
for(register int i=1;i<=n;++i)add(S,i,1);
for(register int i=1;i<=n;++i)add(i+n,T,1);
for(register int i=1;i<=m;++i){
register int a,b;read(a),read(b);
add(a,b+n,1);
}
register int ans = dinic();
int top=0;
for(register int i=1;i<=n;++i){//随便枚举一个点,然后先往上走,再往下走
if(pd[i]==1)continue;
top=0;
register int now = i;pd[now]=1;
while(1){
for(register int j=head[now+n];~j;j=nxt[j])
if(w[j^1]==0&&to[j]!=T){
stk[++top]=to[j];
now = to[j];
break;
}
if(pd[now]==1)break;
pd[now]=1;
}
while(top){
cout<<stk[top]<<' ';top--;
}
cout<<i<<' ';now=i;
while(1){
for(register int j=head[now];~j;j=nxt[j]){
if(w[j]==0&&to[j]!=S&&to[j]!=T){
now=to[j]-n;break;
}
}
if(pd[now]==1)break;
pd[now]=1;cout<<now<<' ';
}
cout<<endl;
}
cout<<n-ans<<endl;
}

981

被折叠的 条评论
为什么被折叠?



