• 网络流专题



    很明显的一道最大流匹配题目
    唯一要注意的点是题目要求是一头牛只能搭配一个饮料和事物
    所以要拆点

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define ll long long
    #define INF 0x7fffffff
    const int maxn=505;
    const int maxm=200000;
    int N,F,D,cnt,S,T;
    int head[maxm],num[maxn],maxflow;
    struct node{
    	int to,next,w;
    }edg[maxm];
    void add(int u,int v,int w){
    	++cnt;
    	edg[cnt].to=v;
    	edg[cnt].w=w;
    	edg[cnt].next=head[u];
    	head[u]=cnt;
    }
    queue<int>Q;
    bool bfs(){
    	while(!Q.empty()){
    	   Q.pop();
    	}
    	for(int i=S;i<=T+N;i++)num[i]=-1;
    	num[S]=1;
    	Q.push(S);
    	while(!Q.empty()){
    		int now=Q.front();Q.pop();
    		for(int i=head[now];i;i=edg[i].next){
    		int to=edg[i].to,w=edg[i].w;
    		if(w&&num[to]==-1){
    			num[to]=num[now]+1;
    			Q.push(to);
    		}
    		} 
    	} 
    	return (num[T]!=-1);
    }
    int dfs(int u,int f){
    	if(u==T){
    		return f;
    	}
    	int flow;
    	for(int i=head[u];i;i=edg[i].next){
    		int to=edg[i].to,w=edg[i].w;
    		if(w&&num[to]==num[u]+1&&(flow=dfs(to,min(f,w)))){
    			edg[i].w-=flow;
    			edg[i^1].w+=flow;
    			return flow;
    		}
    	}
    	return 0;
    }
    void dinic(){
    	int minn;
    	while(bfs()){
    		while(minn=dfs(S,INF)){
    			maxflow+=minn;
    		}
    	}
    }
    int main(){
    	scanf("%d%d%d",&N,&F,&D);
    	cnt++;
    	S=1,T=1+N+F+D+1;
    	for(int i=1;i<=F;i++)add(S,i+1,1),add(i+1,S,0);
    	for(int i=1;i<=D;i++)add(i+1+F+N,T,1),add(T,i+1+F+N,0);
    	for(int i=1;i<=N;i++)add(1+F+i,1+F+N+D+1+i,1),add(1+F+N+D+i+1,1+F+i,0);
    	for(int i=1;i<=N;i++){
    		int ff,dd;
    		scanf("%d%d",&ff,&dd);
    		for(int t,j=1;j<=ff;j++){
    			scanf("%d",&t);
    			add(t+1,1+F+i,1);
    			add(1+F+i,t+1,0);
    		}
    		for(int t,j=1;j<=dd;j++){
    			scanf("%d",&t);
    			add(1+F+N+D+1+i,1+F+N+t,1);
    			add(1+F+N+t,1+F+N+D+1+i,0);
    		}
    	}
    	dinic();
    	printf("%d\n",maxflow);
         return 0;
    }
    
    

    其实网络流的题目还是很明显的
    你读完题目就知道这个是网络流的题目
    建边
    超源点S连接每个试题,因为每个试题只能选一次,所以边权为1
    每个试题连接多个不同的类型,因为每个试题只能代表一种类型,所以边权为1
    每个类型连接超汇点T,因为每个类型有不同的要求数量,所以边权具体题目给出
    输出
    这个题关键就是输出
    记得dinic算法是建立了反向边的,所以只要该类型通向试题的反向边权大于0,就可以输出

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define ll long long
    #define INF 0x7fffffff
    const int maxn=2e3+5;
    const int maxm=1e6+5;
    int K,N,M,S,T,ans,cnt;
    int head[maxm],num[maxn],vis[maxn];
    queue<int>Q;
    struct node{
    	int to,w,next;
    }edg[maxm];
    void add(int u,int v,int w){
    	++cnt;
    	edg[cnt].to=v;
    	edg[cnt].w=w;
    	edg[cnt].next=head[u];
    	head[u]=cnt;
    }
    bool bfs(){
    	while(!Q.empty()){
    		Q.pop();
    	}
    	for(int i=S;i<=T;i++)vis[i]=-1;
    	vis[S]=1;
    	Q.push(S);
    	while(!Q.empty()){
    		int u=Q.front();
    		Q.pop();
    		for(int i=head[u];i;i=edg[i].next){
    			int to=edg[i].to,w=edg[i].w;
    			if(w&&vis[to]==-1){
    				vis[to]=vis[u]+1;
    				Q.push(to);
    			}
    		}
    	}
    	return (vis[T]!=-1);
    }
    int dfs(int u,int f){
    	if(u==T)return f;
    	int flow=0;
    	for(int i=head[u];i;i=edg[i].next){
    		int to=edg[i].to,w=edg[i].w;
    		if(w&&vis[to]==vis[u]+1&&(flow=dfs(to,min(w,f)))){
    			edg[i].w-=flow;
    			edg[i^1].w+=flow;
    			return flow;
    		}
    	}
    	return 0;
    }
    void dinic(){
    	while(bfs()){
    		int ff;
    		while(ff=dfs(S,INF)){
    			ans+=ff;
    		}
    	}
    }
    void print(int u){
    	for(int i=head[u];i;i=edg[i].next){
    		int to=edg[i].to,w=edg[i].w;
    		if(w>0&&to<=S+N)
    		printf(" %d",to-S);
    	}
    }
    int main(){
    	scanf("%d%d",&K,&N);
    	++cnt;S=1;T=1+N+K+1;
    	for(int i=1;i<=K;i++){
    		scanf("%d",&num[i]),M+=num[i];
    		add(S+N+i,T,num[i]);
    		add(T,S+N+i,0);
    	}
    	for(int i=1;i<=N;i++)add(S,S+i,1),add(S+i,S,0);
    	for(int i=1;i<=N;i++){
    		int p;scanf("%d",&p);
    		while(p--){
    			int t;scanf("%d",&t);
    			add(S+i,S+N+t,1);
    			add(S+N+t,S+i,0);
    		}
    	}
    	dinic();
    	if(ans<M){
    		printf("No Solution!\n");
    		return 0;
    	}
    	else {
    		for(int i=1;i<=K;i++){
    			printf("%d:",i);
    			print(i+S+N);
    			printf("\n");
    		}
    	}
         return 0;
    }
    
    


    分析
    很好能get到这个题是最小割的题目
    关键的点就是在于朋友之间建边要双向建边,一开始我一直没想明白,做题的时候也很难想到
    那就说明对最小割的理解还不够深刻
    一般我们建立S->T的单向边是因为只有S流向T,而这个题不一样
    尽管A和B两人的意见不一样,那可以B妥协A,A可以妥协B,就是说
    S可以流向T,T同样可以流向S

    点击查看代码
      #include<bits/stdc++.h>
        #define il inline
        using namespace std;
        const int N=100005,inf=23333333;
        int n,m,s,t=520,h[N],cnt=1,dis[N],ans;
        struct edge{
        int to,net,v;
        }e[N*4];
        il void add(int u,int v,int w)
        {
            e[++cnt].to=v,e[cnt].net=h[u],e[cnt].v=w,h[u]=cnt;
            e[++cnt].to=u,e[cnt].net=h[v],e[cnt].v=0,h[v]=cnt;
        }
        queue<int>q;
        il bool bfs()
        {
            memset(dis,-1,sizeof(dis));
            q.push(s),dis[s]=0;
            while(!q.empty())
            {
                int u=q.front();q.pop();
                for(int i=h[u];i;i=e[i].net)
                if(dis[e[i].to]==-1&&e[i].v>0)dis[e[i].to]=dis[u]+1,q.push(e[i].to);
            }
            return dis[t]!=-1;
        }
        il int dfs(int u,int op)
        {
            if(u==t)return op;
            int used=0;
            for(int i=h[u];i;i=e[i].net)
            {
                int v=e[i].to;
                if(dis[v]==dis[u]+1&&e[i].v>0)
                {
                    used=dfs(v,min(op,e[i].v));
                    if(!used)continue;
                    e[i].v-=used,e[i^1].v+=used;
                   return used;
                }
            }
            return 0;
        }
        int main()
        {
            scanf("%d%d",&n,&m);
            int x,y;
            for(int i=1;i<=n;i++){
                scanf("%d",&x);
                if(x==1)add(s,i,1);
                else add(i,t,1);
            }
            for(int i=1;i<=m;i++){
                scanf("%d%d",&x,&y);
                add(x,y,1),add(y,x,1);
            }
            while(bfs())ans+=dfs(s,inf);
            cout<<ans;
            return 0;
    }
    


    这是最小割的经典题目类型
    最大权闭合图
    建图:
    S与每个实验相连,边权为资金费
    T与每个仪器相连,边权为消耗费用(相当于已经取绝对值了)
    每个实验与相应的仪器相连,边权设为无穷大(因为割边一定不能割实验与仪器的边)
    最后就是跑一遍dinic最大流
    最大流=最小割
    最大权闭合子图的权值和 = max{被选择的点权和} = 正点权和−min{没被选择的正权点之和 + 被选择的负权点绝对值和} = 正点权和−最小割
    最后一个要解决的问题就是要输出这个最大权闭合图
    考虑最后一次bfs,能够到达的点就是在该图内

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define inf 0x7fffffff
    #define ll long long
    const int maxn=155;
    const int maxm=1e6;
    int S,T,M,N,cnt,tot,ans;
    int head[maxn],dp[maxn];
    struct node{
    	int to,w,next;
    }edg[maxm];
    void add(int u,int v,int w){
    	edg[++cnt].next=head[u];edg[cnt].to=v;edg[cnt].w=w;head[u]=cnt;
    	edg[++cnt].next=head[v];edg[cnt].to=u;edg[cnt].w=0;head[v]=cnt;
    }
    queue<int>Q;
    bool bfs(){
    	while(!Q.empty()){
    		Q.pop();
    	}
    	for(int i=S;i<=T;i++)dp[i]=-1;
    	dp[S]=1;
    	Q.push(S);
    	while(!Q.empty()){
    		int u=Q.front();
    		Q.pop();
    		for(int i=head[u];i;i=edg[i].next){
    			int to=edg[i].to,w=edg[i].w;
    			if(w&&dp[to]==-1){
    				dp[to]=dp[u]+1;
    				Q.push(to);
    			}
    		}
    	}
    	return dp[T]!=-1;
    }
    int dfs(int u,int f){
    	if(u==T)return f;
    	int flow=0;
    	for(int i=head[u];i;i=edg[i].next){
    		int to=edg[i].to,w=edg[i].w;
    		if(w&&dp[to]==dp[u]+1&&(flow=dfs(to,min(f,w)))){
    			edg[i].w-=flow;
    			edg[i^1].w+=flow;
    			return flow;
    		}
    	}
    	return 0;
    }
    void dinic(){
    	while(bfs()){
    		int minn;
    		while(minn=dfs(S,inf)){
    			ans+=minn;
    		}
    	}
    	
    }
    int main(){
    	++cnt;
    	S=0;T=150;
    	scanf("%d%d",&M,&N);
        for (int i = 1,c; i <= M; i++) {
    		scanf("%d", &c), tot += c;
    		add(S, i, c);
    		while (getchar() == ' ') {
    			scanf("%d", &c);
    			add(i, c + M, inf);
    		}
    	}
    	for(int i=1,c;i<=N;i++){
    		scanf("%d",&c);
    		add(i+M,T,c);
    	}
    	dinic();
    	for (int i = 1; i <= M; i++) if (dp[i]!=-1) cout << i << ' '; puts("");
    	for (int i = 1; i <= N; i++) if (dp[i + M]!=-1) cout << i << ' '; puts("");
    	printf("%d\n",tot-ans);
         return 0;
    }
    
    


    又是一道最大流模板
    因为每个人只能选一个房间,所以对每个人进行拆点,最后套个dinic模板即可

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define open(s) freopen( s".in", "r", stdin ), freopen( s".out", "w", stdout )
    #define MAXN 405
    #define MAXM 40005
    
    int n, p, q;
    int hd[MAXN], nxt[MAXM << 1], to[MAXM << 1], val[MAXM << 1], tot(1);
    int ans, dis[MAXN];
    queue<int> Q;
    
    int x, y;
    int S, T;
    
    void Add( int x, int y, int z ){ nxt[++tot] = hd[x]; hd[x] = tot; to[tot] = y; val[tot] = z; }
    
    bool BFS(){
    	while( !Q.empty() ) Q.pop();
    	memset( dis, 0, sizeof dis );
    	Q.push(S); dis[S] = 1;
    	while( !Q.empty() ){
    		x = Q.front(); Q.pop();
    		for ( int i = hd[x]; i; i = nxt[i] )
    			if ( val[i] && !dis[to[i]] ){
    				dis[to[i]] = dis[x] + 1;
    				Q.push( to[i] );
    				if ( to[i] == T ) return 1;
    			}
    	}
    	return 0;
    }
    
    int DFS( int x, int fl ){
    	if ( x == T ) return fl;
    	int  k;
    	for ( int i = hd[x]; i ; i = nxt[i] ){
    		if ( val[i] && dis[to[i]] == dis[x] + 1 ){
    			k = DFS( to[i], min( fl, val[i] ) );
    			if(!k)continue;
    			val[i] -= k; val[i^1] += k; 
    			return k;
    		}
    	}
    	return 0;
    }
    
    int main(){
    	scanf( "%d%d%d", &n, &p, &q );
    	S = 0; T = 1 + n + n + p + q;
    	for ( int i = 1; i <= n; ++i ) Add( i, i + n, 1 ), Add( i + n, i, 0 );
    	for ( int i = 1; i <= p; ++i ) Add( S, i + n + n, 1 ), Add( i + n + n, S, 0 );
    	for ( int i = 1; i <= q; ++i ) Add( i + n + n + p, T, 1 ), Add( T, i + n + n + p, 0 );
    	
    	for ( int i = 1; i <= n; ++i )
    		for ( int j = 1; j <= p; ++j ){
    			int t; scanf( "%d", &t );
    			if ( t ) Add( j + n + n, i, 1 ), Add( i, j + n + n, 0 );
    		}
    	for ( int i = 1; i <= n; ++i )
    		for ( int j = 1; j <= q; ++j ){
    			int t; scanf( "%d", &t );
    			if ( t ) Add( i + n, j + n + n + p, 1 ), Add( j + n + n + p, i + n, 0 );
    		}
    	int t;
    	while( BFS() )
    		while( ( t = DFS( S, 0x7f7f7f7f ) ) > 0 ) ans += t;
    	printf( "%d\n", ans );
    	return 0;
    }
    


    这个题目特别点在于要求删点
    那么就拆点:
    x拆为(x,x')容量为1
    原图边x->y 为无向边,所以变为x'->y和y'->x容量均为无穷大
    割点x相当于割掉x->x'这条边
    因为要经过x连通的边没有了x->x'这条边都联通不了
    因为至少满足两个点不连通整个图就不联通了
    最后枚举源点和汇点即可

    点击查看代码
    const int N = 100, M = 5e4+7, INF = 0x3f3f3f3f;
    int s1,t1,n,m;
    int head[N<<1],ver[M],nex[M],edge[M],tot;
    int a[N * N],b[N * N],deep[N<<1];
    
    inline void add(int x,int y,int z){//正边反边
        ver[++tot] = y;edge[tot] = z;
        nex[tot] = head[x];head[x] = tot;
        ver[++tot] = x;edge[tot] = 0;
        nex[tot] = head[y];head[y] = tot;
    }
    
    inline bool bfs(){
        memset(deep,0,sizeof deep);
        queue<int>q;
        q.push(s1);
        deep[s1] = 1;//分层
        while(q.size()){
            int x = q.front();
            q.pop();
            for(int i = head[x];i;i = nex[i]){
                int y = ver[i],z = edge[i];//剩余容量>0才属于残量网络
                if(z > 0 && !deep[y]){//不只是更新deep数组,是在残量网络上更新deep数组
                    q.push(y);
                    deep[y] = deep[x] + 1;
                    if(y == t1)return true;
                }
            }
        }
        return false;
    }
    
    inline int dinic(int x,int flow){
        if(x == t1)return flow;
        int res = flow;
        for(int i = head[x];i && res;i = nex[i]){
            int y = ver[i],z = edge[i];
            if(z > 0 && (deep[y] == deep[x] + 1)){
                int k = dinic(y,min(res,z));
                if(!k)deep[y] = 0;
                edge[i] -= k;
                edge[i ^ 1] += k;
                res -= k;
            }
        }
        return flow - res;
    }
    
    int main(){
        while(cin>>n>>m){
            for(int i = 0;i < m;++i){
                char str[20];
                scanf("%s",str);
                a[i] = b[i] = 0;
                int j;
                for(j = 1;str[j] != ',';j++)
                    a[i] = a[i] * 10 + (str[j] - '0');
                for(j++;str[j] != ')';j++)
                    b[i] = b[i] * 10 + (str[j] - '0');
            }
            int ans = INF;
            for (s1 = 0; s1 < n; s1++)
    		for (t1 = 0; t1 < n; t1++)
            if(s1 != t1){
                memset(head,0,sizeof head);
                tot = 1;
                int maxflow = 0;
                for(int i = 0;i < n;++i){
                    if(i == s1 || i == t1)//i是入点,i+n是出点
                         add(i,i + n,INF);//防止被割断
                    else add(i,i + n,1);
                }
                for(int i = 0;i < m;++i){
                    add(a[i] + n,b[i],INF);//不能割
                    add(b[i] + n,a[i],INF);
                }
                while(bfs()){
                    int num;
                    while((num = dinic(s1,INF)))
                        maxflow += num;
                }
                ans = min(ans,maxflow);
            }
            if(n <= 1 || ans == INF)ans = n;
            cout<<ans<<endl;
        }
        return 0;
    }
    
    
    


    很明显的一道割点题,最后跑一遍最大流就好

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
    	int sss=0,www=1;
    	char chh=getchar();
    	while(chh<'0'||chh>'9'){
    		if(chh=='-') www=-1;
    		chh=getchar();
    	}
    	while(chh>='0'&&chh<='9'){
    		sss=sss*10+chh-'0';
    		chh=getchar();
    	}
    	return sss*www;
    }
    int n,m,q,s,t;
    bool iscut[6005];
    int depth[6005];
    int head[6005],cnt=1;
    struct dj{
    	int to,w,next;
    }edg[1000005];
    void add(int u,int v,int w){
    	edg[++cnt].next=head[u];edg[cnt].to=v;edg[cnt].w=w;head[u]=cnt;
    	edg[++cnt].next=head[v];edg[cnt].to=u;edg[cnt].w=0;head[v]=cnt;
    }
    bool bfs(){
    	for(int i=s;i<=t;i++)depth[i]=-1;
    	depth[s]=1;
    	queue<int> q; q.push(s);
    	while(!q.empty()){
    		int x=q.front(); q.pop();
    		for(register int i=head[x];i;i=edg[i].next){
    			int u=edg[i].to;
    			if(edg[i].w&&depth[u]==-1){
    				depth[u]=depth[x]+1;
    				q.push(u);
    			}
    		}
    	}
    	return depth[t]!=-1;
    }
    int dfs(int now,int flow){
    	if(now==t) return flow;
    	for(register int i=head[now];i;i=edg[i].next){
    		int u=edg[i].to;
    		if(edg[i].w&&depth[u]==depth[now]+1){
    			int tmp=dfs(u,min(edg[i].w,flow));
    			if(!tmp)continue;
    			edg[i].w-=tmp; edg[i^1].w+=tmp;
    			return tmp;
    		}
    	}
    	return 0;
    }
    int Dinic(){
    	int ans=0;
    	while(bfs()) ans+=dfs(s,1e9);
    	return ans;
    }
    int main(){
    	n=read(),m=read(),q=read();
    	s=1,t=2*n+1;
    	add(1,n+1,1e9); //1节点不能被割掉 
    	int u,v;
    	for(register int i=1;i<=m;i++){
    		//图中所给的边 
    		u=read(),v=read();
    		add(u+n,v,1e9); 
    		add(v+n,u,1e9); 
    	}
    	for(register int i=1;i<=q;i++){
    		u=read(); iscut[u]=true;
    		add(u,u+n,1e9);
    		add(u+n,t,1e9);
    	}
    	for(register int i=1;i<=n;i++){
    		if(!iscut[i]){//可以被割掉 
    			add(i,i+n,1); 
    		}
    	}
    	printf("%d",Dinic());
        return 0;
    }
    

    观察又是流动型问题 考虑网络流

    最长时间最小 很明显的二分答案

    建边 将牛棚拆点

    S与每个牛棚i相连,边权为初始牛的数量

    T与每个牛棚n+i相连,边权为最大容量

    floyed预处理两点的最短路 二分的答案为maxx

    如果dis[i,j]最短路<maxx

    建边i->j 边权为inf 特别的i==j的时候 也要建边 边权也为inf

    再跑一遍dinic即可

    check函数判断最大流和总和牛是否相等

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define ll long long
    #define inf 1e18
    const int maxn=2005;
    const int maxm=15005;
    ll dis[maxn][maxn],have[maxn],hold[maxn];
    int n,m,cnt,S,T,tot,ans;
    int head[maxn*2],dp[maxn*2];
    struct node{
    	int to,next;
    	ll w;
    }edg[maxn*maxn*10];
    void add(int u,int v,ll w){
    	edg[++cnt].next=head[u];edg[cnt].w=w;edg[cnt].to=v;head[u]=cnt;
    	edg[++cnt].next=head[v];edg[cnt].w=0;edg[cnt].to=u;head[v]=cnt;
    }
    void floyed();
    void init(){
    	cnt=1;
    	for(int i=S;i<=T;i++)head[i]=0;
    }
    void rebuild(ll maxx){
    	for(int i=1;i<=n;i++){
    		add(S,i,have[i]);
    		add(i+n,T,hold[i]);
    		for(int j=1;j<=n;j++)
    		if(i==j||dis[i][j]<=maxx)
    		add(i,n+j,inf);
    	}
    }
    queue<int>Q;
    bool bfs(){
    	while(!Q.empty())Q.pop();
    	for(int i=S;i<=T;i++)dp[i]=-1;
    	dp[S]=1;Q.push(S);
    	while(!Q.empty()){
    		int u=Q.front();
    		Q.pop();
    		for(int i=head[u];i;i=edg[i].next){
    			int to=edg[i].to;ll w=edg[i].w;
    			if(w&&dp[to]==-1){
    				dp[to]=dp[u]+1;
    				Q.push(to);
    			}
    		}
    	}
    	return dp[T]!=-1;
    }
    ll dfs(int u,ll f){
    	if(u==T)return f;
    	ll flow;
    	for(int i=head[u];i;i=edg[i].next){
    		int to=edg[i].to;ll w=edg[i].w;
    		if(w&&dp[to]==dp[u]+1){
    			flow=dfs(to,min(f,w));
    			if(!flow)continue;
    			edg[i].w-=flow;
    			edg[i^1].w+=flow;
    			return flow;
    		}
    	}
    	return 0;
    }
    ll dinic(){
    	ll res=0;
    	while(bfs())res+=dfs(S,inf);
    	return res;
    }
    bool ck(ll aa){
    	return aa==tot;
    }
    int main(){
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)
    	cin>>have[i]>>hold[i],tot+=have[i];
    	for(int i=1;i<=n;i++)
    	for(int j=1;j<=n;j++)
    	dis[i][j]=inf;
    	for(ll i=1,ww,uu,vv;i<=m;i++){
    		cin>>uu>>vv>>ww;
    		dis[uu][vv]=min(dis[uu][vv],ww);
    		dis[vv][uu]=min(dis[vv][uu],ww);
    	}
    	floyed();
    	S=0,T=n<<1|1;
    	ll mid,l=0,r=inf;
    	while(l<=r){
    		mid=(l+r)>>1;
    		init();
    		rebuild(mid);
    		if(ck(dinic()))
    		r=mid-1,ans=mid;
    		else l=mid+1;
    	}
    	if(ans)cout<<ans<<endl;
    	else cout<<-1<<endl; 
         return 0;
    }
    void floyed(){
    	for(int k=1;k<=n;k++)
    	for(int i=1;i<=n;i++)
    	if(i!=k)
    	for(int j=1;j<=n;j++)
    	if(j!=i&&j!=k)
    	dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    }
    

    这个题没啥好说的 就是妥妥的最大流模板题 唯一要注意的是 re可能是数组越界 也有可能分母为0 !!!

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define ll long long
    #define inf 1e9
    const int maxn=2005;
    const int maxm=20005;
    int dp[maxn],head[maxn];
    int cnt=1,S,T,N,M,X;
    struct node{
    	int to,next,w;
    }edg[maxm];
    void add(int u,int v,int w){
    	edg[++cnt].next=head[u];head[u]=cnt;edg[cnt].to=v;edg[cnt].w=w;
    	edg[++cnt].next=head[v];head[v]=cnt;edg[cnt].to=u;edg[cnt].w=0;
    }
    queue<int>Q;
    bool bfs(){
    	while(!Q.empty())Q.pop();
    	for(int i=S;i<=T;i++)dp[i]=-1;
    	dp[S]=1;Q.push(S);
    	while(!Q.empty()){
    		int u=Q.front();Q.pop();
    		for(int i=head[u];i;i=edg[i].next){
    			int to=edg[i].to,w=edg[i].w;
    			if(w&&dp[to]==-1){
    				dp[to]=dp[u]+1;
    				Q.push(to);
    			}
    		}
    	}
    	return dp[T]!=-1;
    }
    int dfs(int u,int f){
    	if(u==T)return f;
    	int flow=0;
    	for(int i=head[u];i;i=edg[i].next){
    		int to=edg[i].to,w=edg[i].w;
    		if(w&&dp[to]==dp[u]+1){
    		   flow=dfs(to,min(f,w));
    		   if(!flow)continue;
    		   edg[i].w-=flow;
    		   edg[i^1].w+=flow;
    		   return flow;	
    		}
    		 
    	}
    	return 0;
    }
    ll dinic(){
    	ll res=0;
    	while(bfs())res+=dfs(S,inf);
        return res;
    }
    int main(){
    	cin>>N>>M>>X;
    	S=1,T=N;
    	for(int i=1;i<=M;i++){
    		int uu,vv,ww;
    		cin>>uu>>vv>>ww;
    		add(uu,vv,ww);
    	}
    	ll ans=dinic();
    	if(!ans){
    		cout<<"Orz Ni Jinan Saint Cow!"<<endl;
    		return 0;
    	}
    	ll sum=X/ans;
    	if(X%ans)sum++;
    	cout<<ans<<" "<<sum<<endl;
         return 0;
    }
    
    

    假设先取所有的格子 现在我们目标就是减去最小的 使得剩下的和最大

    现在将(i+j)%2分为奇偶两种集合 和(i,j)相邻的奇偶性一定和(i,j)不同

    我们将S与偶数相连 边权为点权 T与奇数相连 边权为点权

    再将偶数与相邻奇数相连 边权为inf 因为这条边时不能割的

    最后答案就是ans-dinic

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define inf 2e9
    #define ll long long
    const int maxn=100005;
    int m,n,S,T,cnt=1,ans;
    int head[maxn],dp[maxn],val[maxn];
    int d[5][2];
    struct node{
    	int to,next,w;
    }edg[maxn*10];
    void add(int u,int v,int w){
    	edg[++cnt].next=head[u];head[u]=cnt;edg[cnt].to=v;edg[cnt].w=w;
    	edg[++cnt].next=head[v];head[v]=cnt;edg[cnt].to=u;edg[cnt].w=0;
    }
    queue<int>Q;
    bool bfs(){
    	while(!Q.empty())Q.pop();
    	for(int i=S;i<=T;i++)dp[i]=-1;
    	dp[S]=1;Q.push(S);
    	while(!Q.empty()){
    		int u=Q.front();Q.pop();
    		for(int i=head[u];i;i=edg[i].next){
    			int to=edg[i].to,w=edg[i].w;
    			if(w&&dp[to]==-1){
    				dp[to]=dp[u]+1;
    				Q.push(to);
    			}
    		}
    	}
    	return dp[T]!=-1;
    }
    int dfs(int u,int f){
    	if(u==T)return f;
    	int flow=0;
    	for(int i=head[u];i;i=edg[i].next){
    		int to=edg[i].to,w=edg[i].w;
    		if(w&&dp[to]==dp[u]+1){
    			flow=dfs(to,min(f,w));
    			if(!flow)continue;
    			edg[i].w-=flow;
    			edg[i^1].w+=flow;
    			return flow;
    		}
    	}
    	return 0;
    }
    int dinic(){
    	int res=0;
    	while(bfs())res+=dfs(S,inf);
    	return res;
    }
    bool ck(int x,int y){
    	if(x<1||x>m)return false;
    	if(y<1||y>n)return false;
    	return true;
    } 
    int main(){
    	cin>>m>>n;
    	for(int vv,i=1;i<=m;i++)
    	for(int j=1;j<=n;j++)
    	cin>>vv,val[(i-1)*n+j]=vv,ans+=vv;
    	S=0,T=n*m+1;
    	for(int i=1;i<=m;i++)
    	for(int j=1;j<=n;j++)
    	if((i+j)&1)add(S,(i-1)*n+j,val[(i-1)*n+j]);
    	else add((i-1)*n+j,T,val[(i-1)*n+j]);
    	d[1][0]=-1,d[1][1]=0;
    	d[2][0]=0,d[2][1]=-1;
    	d[3][0]=1,d[3][1]=0;
    	d[4][0]=0,d[4][1]=1;
    	for(int i=1;i<=m;i++)
    	for(int j=1;j<=n;j++)
    	if((i+j)&1)
    		for(int k=1;k<=4;k++){
    			int xx=i+d[k][0],yy=j+d[k][1];
    			if(!ck(xx,yy))continue;
    			add((i-1)*n+j,(xx-1)*n+yy,inf);
    		}
    	cout<<ans-dinic()<<endl; 
         return 0;
    }
    
    

    因为是无向图 所以要建立双向边 再跑一遍最大流 很容易tle要加各种优化

    点击查看代码
    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <cctype>
    using namespace std;
    inline void read(int &x) {
        x = 0; char c = getchar();
        while(!isdigit(c)) c = getchar();
        while(isdigit(c)) x = (x << 3) + (x << 1) + c - '0', c = getchar();
    }
    #define MAXN 1003
    struct node{
        int fr, to, va, nxt;
    }edge[MAXN * MAXN * 6];
    int head[MAXN * MAXN], cnt;
    inline void add_edge(int u, int v, int w) {
        edge[cnt].fr = u, edge[cnt].to = v, edge[cnt].va = w;
        edge[cnt].nxt = head[u], head[u] = cnt++;
        edge[cnt].fr = v, edge[cnt].to = u, edge[cnt].va = w;
        edge[cnt].nxt = head[v], head[v] = cnt++; //反向边初始化
    }
    int st, ed, rk[MAXN * MAXN];
    int BFS() {
        queue<int> q;
        memset(rk, 0, sizeof rk);
        rk[st] = 1;
        q.push(st);
        while(!q.empty()) {
            int tmp = q.front();
            //cout<<tmp<<endl;
            q.pop();
            for(int i = head[tmp]; i != -1; i = edge[i].nxt) {
                int o = edge[i].to;
                if(rk[o] || edge[i].va <= 0) continue;
                rk[o] = rk[tmp] + 1;
                q.push(o);
            }
        }
        return rk[ed];
    }
    int dfs(int u, int flow) {
        if(u == ed) return flow;
        for(int i = head[u]; i != -1 && add < flow; i = edge[i].nxt) {
            int v = edge[i].to;
            if(rk[v] != rk[u] + 1 || !edge[i].va) continue;
            int tmpadd = dfs(v, min(edge[i].va, flow));
            if(!tmpadd) {  //重要!就是这里!
                rk[v] = -1;
                continue;
            }
            edge[i].va -= tmpadd, edge[i ^ 1].va += tmpadd;
            return tmpadd;
        }
        return 0;
    }
    int ans;
    void dinic() {
        while(BFS()) ans += dfs(st, 0x3fffff); 
    }
    int n, m;
    inline int gethash(int i, int j) {
        return (i - 1) * m + j;
    }
    int main() {
        memset(head, -1, sizeof head);
        read(n), read(m);
        int tmp;
        st = 1, ed = gethash(n, m);
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j < m; ++j)
                read(tmp), add_edge(gethash(i, j), gethash(i, j + 1), tmp);
        }
        for(int i = 1; i < n; ++i) {
            for(int j = 1; j <= m; ++j) 
                read(tmp), add_edge(gethash(i, j), gethash(i + 1, j), tmp);
        }
        for(int i = 1; i < n; ++i) {
            for(int j = 1; j < m; ++j) 
                read(tmp), add_edge(gethash(i, j), gethash(i + 1, j + 1), tmp);
        }
        dinic();
        cout<<ans<<endl;
        return 0;
    } 
    
    

    费用流的板子题目 和dinic差别在于 bfs换成了spfa dfs过程变成了从T回溯的过程

    先回顾一下spfa:

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define ll long long
    const int maxn=1e5+5;
    int cnt=1,n,m,S,T,maxflow,mincost;
    struct node{
    	int to,next,flow,dis;
    }edg[maxn];
    bool vis[maxn];
    int dis[maxn],last[maxn],flow[maxn],pre[maxn],head[maxn];
    void add(int u,int v,int flow,int dis){
    	edg[++cnt].next=head[u];
    	edg[cnt].to=v;
    	head[u]=cnt;
    	edg[cnt].flow=flow;
    	edg[cnt].dis=dis;
    }
    queue<int>Q;
    bool spfa(){
    	memset(dis,0x7f,sizeof(dis));
    	memset(flow,0x7f,sizeof(flow));
    	memset(vis,0,sizeof(vis));
    	Q.push(S);
    	vis[S]=1;dis[S]=0;pre[T]=-1;
    	while(!Q.empty()){
    		int u=Q.front();
    		Q.pop();
    		vis[u]=0;
    		for(int i=head[u];i;i=edg[i].next){
    			int to=edg[i].to;
    			if(edg[i].flow&&dis[to]>dis[u]+edg[i].dis){
    				dis[to]=dis[u]+edg[i].dis;
    				pre[to]=u;
    				last[to]=i;
    				flow[to]=min(flow[u],edg[i].flow);
    				if(!vis[to]){
    					vis[to]=1;
    					Q.push(to);
    				}
    			}
    		}
    	}
    	return pre[T]!=-1;
    } 
    void dd(){
    	while(spfa()){
    		int now=T;
    		maxflow+=flow[T];
    		mincost+=flow[T]*dis[T];
    		while(now!=S){
    			edg[last[now]].flow-=flow[T];
    			edg[last[now]^1].flow+=flow[T];
    			now=pre[now];
    		}
    	}
    }
    int main(){
    	cin>>n>>m>>S>>T;
    	for(int i=1;i<=m;i++){
    		int xx,yy,zz,ff;
    		scanf("%d%d%d%d",&xx,&yy,&zz,&ff);
    		add(xx,yy,zz,ff);
    		add(yy,xx,0,-ff);
    	}
    	dd();
    	cout<<maxflow<<" "<<mincost<<endl;
         return 0;
    }
    
    

    很好很经典的一道题

    我开始一直没想明白为什么S要和旧毛巾连ri的边 第一天的旧毛巾是哪里来的 第一天不是只能买新毛巾嘛?

    观察图才发现 点1+n(第一天的新毛巾来源只有买,没有其他的来源了) S向旧毛巾连ri的边只是保证每天用完肯定是会有ri的旧毛巾的

    这个建图确实不好想 但是真的好巧妙

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define ll long long
    #define inf 1e9
    const int maxn=2005;
    int n,S,T;
    int head[maxn<<1],cnt=1;
    int d[maxn<<1],vis[maxn<<1],last[maxn<<1],pre[maxn<<1],flow[maxn<<1],r[maxn];
    ll mincost;
    struct node{
    	int to,next;
    	int f,cost;
    }edg[maxn*maxn];
    void add(int u,int v,int f,int w){
    	edg[++cnt].next=head[u];head[u]=cnt;edg[cnt].to=v;edg[cnt].cost=w;edg[cnt].f=f;
    	edg[++cnt].next=head[v];head[v]=cnt;edg[cnt].to=u;edg[cnt].cost=-w;edg[cnt].f=0;
    } 
    queue<int>Q;
    bool spfa(){
    	memset(d,0x7f,sizeof(d));
    	memset(flow,0x7f,sizeof(flow));
    	memset(vis,0,sizeof(vis));
    	vis[S]=1;d[S]=0;Q.push(S);pre[T]=-1;
    	while(!Q.empty()){
    		int u=Q.front();
    		Q.pop();vis[u]=0;
    		for(int i=head[u];i;i=edg[i].next){
    			int to=edg[i].to,f=edg[i].f,cost=edg[i].cost;
    		    if(f&&d[to]>d[u]+cost){
    		    	d[to]=d[u]+cost;
    		    	pre[to]=u;
    		    	last[to]=i;
    		    	flow[to]=min(flow[u],f);
    		    	if(!vis[to]){
    		    		vis[to]=1;
    		    		Q.push(to);
    				}
    			}	
    		}
    	}
    	return pre[T]!=-1;
    }
    void calc(){
    	while(spfa()){
    		int now=T;
    		mincost+=flow[T]*d[T];
    		while(now!=S){
    			edg[last[now]].f-=flow[T];
    			edg[last[now]^1].f+=flow[T];
    			now=pre[now];
    		}
    	}
    }
    int main(){
    	cin>>n;
    	S=0,T=(n<<1)+1;
    	for(int i=1;i<=n;i++)cin>>r[i];
    	int pp,mm,ff,nn,ss;
    	cin>>pp>>mm>>ff>>nn>>ss;
    	for(int i=1;i<=n;i++){
    		add(S,i,r[i],0);
    		add(i+n,T,r[i],0);
    		add(S,n+i,inf,pp);
    		if(i<n)add(i,i+1,inf,0);
    		if(i+mm<=n)add(i,i+n+mm,inf,ff);
    		if(i+nn<=n)add(i,i+n+nn,inf,ss);
    	}
    	calc();
    	cout<<mincost<<endl;
         return 0;
    }
    
    

    这个题题很明显的求最小费用最大流和最大费用最大流

    求最大费用最大流只需要将边权都取为相反数就好,为什么呢?

    以前边权是求最小正值 现在边权是求小负值 就是负数的绝对值最大 就是最长路

    因为要求两次 所以进行一次后就直接将边复原再跑一次就好了

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define ll long long
    #define inf 1e9
    const int maxn=2005;
    int head[maxn],cnt=1;
    struct node{
    	int to,next,w,f;
    }edg[maxn*maxn*10];
    void add(int u,int v,int f,int w){
    	edg[++cnt].w=w;edg[cnt].f=f;edg[cnt].next=head[u];head[u]=cnt;edg[cnt].to=v;
    	edg[++cnt].w=-w;edg[cnt].f=0;edg[cnt].next=head[v];head[v]=cnt;edg[cnt].to=u;
    }
    int vis[maxn],flow[maxn],d[maxn],pre[maxn],last[maxn],dd[maxn];
    int n,m,S,T;
    queue<int>Q;
    bool spfa(){
    	memset(vis,0,sizeof(vis));
    	memset(flow,0x7f,sizeof(flow));
    	memset(d,0x7f,sizeof(d));
    	pre[T]=-1;vis[S]=1;Q.push(S);d[S]=0;
    	while(!Q.empty()){
    		int u=Q.front();
    		Q.pop();vis[u]=0;
    		for(int i=head[u];i;i=edg[i].next){
    			int to=edg[i].to,f=edg[i].f,w=edg[i].w;
    			if(f&&d[to]>d[u]+w){
    				d[to]=d[u]+w;
    				pre[to]=u;
    				last[to]=i;
    				flow[to]=min(flow[u],f);
    				if(!vis[to]){
    					Q.push(to);
    					vis[to]=1;
    				}
    			}
    		}
    	}
    	return pre[T]!=-1;
    }
    int calc(){
    	int cost=0;
    	while(spfa()){
    		int now=T;
    		cost+=flow[T]*d[T];
    		while(now!=S){
    			edg[last[now]].f-=flow[T];
    			edg[last[now]^1].f+=flow[T];
    			now=pre[now];
    		}
    	}
    	return cost;
    }
    int main(){
    	cin>>m>>n;
    	S=0,T=n*m+1;
    	for(int ai,i=1;i<=m;i++)
    	cin>>ai,add(S,i,ai,0);
    	for(int bi,i=1;i<=n;i++)
    	cin>>bi,add(m+i,T,bi,0);
    	for(int xx,i=1;i<=m;i++)
    	for(int j=1;j<=n;j++)
    	cin>>xx,add(i,m+j,inf,xx);
        cout<<calc()<<endl;
        for(int i=2;i<cnt;i+=2){
        	edg[i].f+=edg[i^1].f;
        	edg[i^1].f=0;
        	edg[i].w=-edg[i].w;
        	edg[i^1].w=-edg[i^1].w;
    	}
    	cout<<-calc()<<endl;
    	return 0;
    }
    
    

    点击查看代码
    #include<bits/stdc++.h>
    #define inf 1000000007
    #define N 2000005
    #define M 505
    using namespace std;
    struct Edge{
        int u,v,next,f;
    }G[N];
    int head[N],tot=0,a[M],dp[M],n,len,s,t,ans;
    void addedge(int u,int v,int f){
        G[tot].u=u;G[tot].v=v;G[tot].f=f;G[tot].next=head[u];head[u]=tot++;
        G[tot].u=v;G[tot].v=u;G[tot].f=0;G[tot].next=head[v];head[v]=tot++;
    }
    int level[100*M];
    bool bfs(int s,int t){
        memset(level,0,sizeof(level));
        queue<int>q;q.push(s);level[s]=1;
        while(!q.empty()){
            int u=q.front();q.pop();
            if(u==t)return 1;
            for(int i=head[u];i!=-1;i=G[i].next){
                int v=G[i].v,f=G[i].f;
                if(level[v]==0&&f)q.push(v),level[v]=level[u]+1;
            }
        }
        return 0;
    }
    int dfs(int u,int maxf,int t){
        if (u==t)return maxf;
        int rat=0;
        for (int i=head[u];i!=-1&&rat<maxf;i=G[i].next){
            int v=G[i].v;int f=G[i].f;
            if (level[v]==level[u]+1&&f){
                int Min=min(maxf-rat,f);
                f=dfs(v,Min,t);
                G[i].f-=f;G[i^1].f+=f;rat+=f;
            }
        }
        if (!rat)level[u]=N;
        return rat;
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),dp[i]=1;
        for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
        if(a[j]<=a[i])dp[i]=max(dp[i],dp[j]+1);
        for(int i=1;i<=n;i++)len=max(len,dp[i]);
        printf("%d\n",len);
        if(len==1){
        	cout<<n<<endl<<n<<endl;
        	return 0;
    	}
        s=0;t=5000;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++)if(dp[i]==1)addedge(s,i,1);
        for(int i=1;i<=n;i++)if(dp[i]==len)addedge(i+n,t,1);
        for(int i=1;i<=n;i++)addedge(i,i+n,1);
        for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
        if(a[j]<=a[i]&&dp[j]+1==dp[i])addedge(j+n,i,1);
        while(bfs(s,t))ans+=dfs(s,inf,t);printf("%d\n",ans);
        addedge(1,1+n,inf);addedge(s,1,inf);
        if(dp[n]==len)addedge(n,n*2,inf),addedge(n*2,t,inf);
        while(bfs(s,t))ans+=dfs(s,inf,t);
        printf("%d\n",ans);
        return 0;
    }
    

    这个题目恶心就在于中间建边的过程

    点击查看代码
    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 751 * 15 + 10;
    const int M = (N + 750 + 20 * 751 + 10) * 2;
    const int INF = 1e9; 
    
    int n, m, k, S, T;
    struct Edge
    {
    	int to, nxt, flow;
    }line[M];
    int fist[N], idx;
    int cur[N], d[N];
    struct Ship
    {
    	int h, r, id[30];
    }ships[30];
    int fa[30];
    
    int find(int x)
    {
    	if(fa[x] != x) fa[x] = find(fa[x]);
    	return fa[x];
    }
    
    int get(int i, int day)
    {
    	return day * (n + 2) + i;	
    }
    
    void add(int x, int y, int z)
    {
    	line[idx] = {y, fist[x], z};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], 0};
    	fist[y] = idx ++;
    }
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v);
    			}
    		}
    	}	
    	return 0;
    } 
    
    int find(int u, int limit)
    {
    	if(u == T) return limit;
    	int flow = 0;
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow)
    		{
    			int t = find(v, min(line[i].flow, limit - flow));
    			if(!t) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int res = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) res += flow;
    	return res;	
    }
    
    int main()
    {
    	scanf("%d%d%d", &n, &m, &k);
    	S = N - 2, T = N - 1;
    	memset(fist, -1, sizeof fist);
    	for(int i = 0; i < 30; ++ i) fa[i] = i;
    	for(int i = 0; i < m; ++ i)
    	{
    		int a, b;
    		scanf("%d%d", &a, &b);
    		ships[i] = {a, b};
    		for(int j = 0; j < b; ++ j)
    		{
    			int id;
    			scanf("%d", &id);
    			if(id == -1) id = n + 1; 
    			ships[i].id[j] = id;
    			if(j) 
    			{
    				int x = ships[i].id[j - 1];
    				fa[find(x)] = find(id);
    			}
    		}
    	}
    	if(find(0) != find(n + 1)) puts("0");
    	else
    	{
    		add(S, get(0, 0), k);
    		add(get(n + 1, 0), T, INF);
    		int day = 1, res = 0; 
    		while(1)
    		{
    			add(get(n + 1, day), T, INF);
    			for(int i = 0; i <= n + 1; ++ i)
    				add(get(i, day - 1), get(i, day), INF);
    			for(int i = 0; i < m; ++ i)
    			{
    				int r = ships[i].r;
    				int a = ships[i].id[(day - 1) % r];
    				int b = ships[i].id[day % r];
    				add(get(a, day - 1), get(b, day), ships[i].h);
    			} 
    			res += dinic();
    			if(res >= k) break;
    			++ day;
    		}
    		printf("%d\n", day);
    	}
    	return 0;
    }
    
    

    最小路径覆盖数=顶点数-最大匹配

    为什么?

    我们首先将原图用n条路径覆盖,每条边只经过每个节点。

    现在尽量合并更多的路径(即将两个路径通过一条边首尾相连)。

    可以知道,每合并两条路径,图中的路径覆盖数就会减少1。

    所以我们只需要利用网络流合并相关的路径即可

    本题麻烦就在 输出路径 只要我们找起点 之后在残留网络里面走就好

    问题变为维护起点 可以在dfs遍历的过程中记录 也可以最后并查集记录

    我这里用的并查集 合并两个的时候 一定是fa[find(to)]=find(u) 顺序不能变 因为一定要保证起点为fa[i]=i

    判断起点就直接fa[i]=i即可

    还有数组能开大点就开大点

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define ll long long
    #define inf 1e9
    const int maxn=3550;
    const int maxm=(maxn<<2)+6e5;
    int S,T,n,m;
    int dp[maxn],vis[maxn],fa[maxn];
    int head[maxn],cnt=1;
    int find(int x){
    	if(fa[x]!=x)return fa[x]=find(fa[x]);
    	return x;
    }
    struct node{
    	int to,next,f;
    }edg[maxn];
    void add(int u,int v,int f){
    	edg[++cnt].next=head[u];head[u]=cnt;edg[cnt].f=f;edg[cnt].to=v;
    	edg[++cnt].next=head[v];head[v]=cnt;edg[cnt].f=0;edg[cnt].to=u;
    }
    queue<int>Q;
    bool bfs(){
    	while(!Q.empty())Q.pop();
    	memset(dp,-1,sizeof(dp));
    	dp[S]=1;
    	Q.push(S);
    	while(!Q.empty()){
    		int u=Q.front();
    		Q.pop();
    		for(int i=head[u];i;i=edg[i].next){
    			int to=edg[i].to,f=edg[i].f;
    			if(f&&dp[to]==-1){
    				dp[to]=dp[u]+1;
    				Q.push(to);
    			}
    		}
    		
    	}
    	return dp[T]!=-1;
    }
    int dfs(int u,int flow){
    	if(u==T)return flow;
    	int tot=0;
    	for(int i=head[u];i;i=edg[i].next){
    		int to=edg[i].to,f=edg[i].f;
    		if(dp[to]==dp[u]+1&&f){
    			int t=dfs(to,min(f,flow-tot));
    			if(!t)continue;
    			edg[i].f-=t;
    			edg[i^1].f+=t;
    			tot+=t; 
    		}
    	}
    	return tot;
    }
    void output(int x){
    	cout<<x<<" ";
    	for(int i=head[x];i;i=edg[i].next)
    		if(edg[i].to>n&&edg[i].to<T&&edg[i].f==0){
    			output(edg[i].to-n);
    		}
    }
    int dinic(){
    	int res=0;
    	while(bfs())
    		res+=dfs(S,inf);
    	for(int i=S;i<=T;i++)
    	fa[i]=i;
    	for(int i=1;i<=n;i++)
    	for(int j=head[i];j;j=edg[j].next){
    		if(edg[j].f==0&&edg[j].to<T&&edg[j].to>n)
    		fa[find(edg[j].to-n)]=find(i);
    	}
    	for(int i=1;i<=n;i++)
    	if(fa[i]==i)
    	output(i),cout<<endl; 
        return res;
    }
    int main(){
    	cin>>n>>m;
    	S=0;T=2*n+1;
    	for(int i=1;i<=n;i++)
    	add(S,i,1),add(i+n,T,1);
    	for(int i=1;i<=m;i++){
    		int uu,vv;
    		cin>>uu>>vv;
    		add(uu,vv+n,1);
    	}
    	cout<<n-dinic();
         return 0;
    }
    
    
  • 相关阅读:
    如何面试程序员?
    类似猪八戒网的网站
    存储过程
    一个不错的网站(博客制作参考)
    用触发器来实现级联更新级联删除
    用触发器进行级联删除
    数据库触发器详解
    浅谈数据库中的存储过程
    JDBC连接数据库
    Java递归函数
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/16044871.html
Copyright © 2020-2023  润新知