zoukankan      html  css  js  c++  java
  • Luogu P4014 分配问题 题解

    闲扯

    蒟蒻的第一道自己想出怎么建图的题!!虽然是一个没什么技术含量的图 想了想,还是写篇题解纪念一下。

    题面

    题面

    Solution

    要求最小费用和最大费用,同时限制了流量,考虑费用流。

    虚拟一个超级源点,从这个点分别向 (N) 个任务连一条流量为 (1) ,费用为 (0) 的边。

    虚拟一个超级汇点,才从 (N) 个物品分别向该点连一条流量为 (1) ,费用为 (0) 的边。

    因为每个人只能做一件,且每个工作只能做一次,所以连的边流量都为一。而第 (i) 个人做第 (j) 个任务获得的贡献为 (val_{i,j}) ,所以从第 (j) 个物品向第 (i) 个人连一条费用为 (val_{i,j}) 的边。

    如果是求最小费用最大流,那么直接跑模板。

    如果是求最大费用最大流,只需要连边时将费用换为负数,求一个最小费用最大流,然后答案再取一个相反数即可。(这个处理好秒啊,自己没想出来,还是看了题解)

    Code

    #include<bits/stdc++.h>
    #define del(a,i) memset(a,i,sizeof(a))
    #define ll long long
    #define inl inline
    #define il inl void
    #define it inl int
    #define ill inl ll
    #define re register
    #define ri re int
    #define rl re ll
    #define mid ((l+r)>>1)
    #define lowbit(x) (x&(-x))
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>il read(T &x){
    	int f=1;char k=getchar();x=0;
    	for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
    	for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
    	x*=f;
    }
    template<class T>il print(T x){
    	if(x/10) print(x/10);
    	putchar(x%10+'0');
    }
    ll mul(ll a,ll b,ll mod){long double c=1.;return (a*b-(ll)(c*a*b/mod)*mod)%mod;}
    it qpow(int x,int m,int mod){
    	int res=1,bas=x%mod;
    	while(m){
    		if(m&1) res=(res*bas)%mod;
    		bas=(bas*bas)%mod,m>>=1;
    	}
    	return res%mod;
    }
    int n,s,t,head[205],num_edge=-1,pre[205],last[205],flow[205],dis[205],mn_cost,val[105][105];
    struct Edge{
    	int next,to,w,c;
    	Edge(){}
    	Edge(int next,int to,int w,int c):next(next),to(to),w(w),c(c){}
    }edge[30000];
    il add_edge(int u,int v,int w,int c){
    	edge[++num_edge]=Edge(head[u],v,w,c),head[u]=num_edge;
    	edge[++num_edge]=Edge(head[v],u,0,-c),head[v]=num_edge;
    }
    bool tr[205];
    inl bool SPFA(int s,int t){
    	queue<int> q;q.push(s);
    	del(dis,0x3f),del(flow,0x3f);
    	dis[s]=0,pre[t]=-1,tr[s]=1;
    	while(!q.empty()){
    		ri pos=q.front();q.pop(),tr[pos]=0;
    		for(ri i=head[pos];i!=-1;i=edge[i].next)
    			if(dis[edge[i].to]>dis[pos]+edge[i].c&&edge[i].w>0){
    				dis[edge[i].to]=dis[pos]+edge[i].c;
    				pre[edge[i].to]=pos,last[edge[i].to]=i;
    				flow[edge[i].to]=min(flow[pos],edge[i].w);
    				if(!tr[edge[i].to]) q.push(edge[i].to),tr[edge[i].to]=1;
    			}
    	}
    	return pre[t]!=-1;
    }
    il MCMF(int s,int t){
    	while(SPFA(s,t)){
    		mn_cost+=dis[t]*flow[t];
    		for(ri u=t;u^s;u=pre[u]) edge[last[u]].w-=flow[t],edge[last[u]^1].w+=flow[t];
    	}
    }
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	read(n),del(head,-1),t=2*n+1;
    	for(ri i=1;i<=n;++i)
    		for(ri j=1;j<=n;++j){
    			read(val[i][j]);
    			add_edge(j,i+n,1,val[i][j]);
    		}
    	for(ri i=1;i<=n;++i) add_edge(s,i,1,0);
    	for(ri i=1;i<=n;++i) add_edge(i+n,t,1,0);
    	MCMF(s,t);
    	printf("%d
    ",mn_cost);
    	del(head,-1),num_edge=-1,mn_cost=0;
    	for(ri i=1;i<=n;++i)
    		for(ri j=1;j<=n;++j)
    			add_edge(j,i+n,1,-val[i][j]);
    	for(ri i=1;i<=n;++i) add_edge(s,i,1,0);
    	for(ri i=1;i<=n;++i) add_edge(i+n,t,1,0);
    	MCMF(s,t);
    	printf("%d",-mn_cost);
    	return 0;
    }
    

    总结

    这道题就是一个板子题,用来入门外加熟悉模板的。

    网络流的建图方式千千万,真的好神奇的,不要满足于现在的成就,还是要多练题,找到自己做网络流的套路呢~~

  • 相关阅读:
    若依问题解决(一)
    Java 将两个List转换为流合并List
    后端返回前端文本换行显示,只能在前端再转换
    Java Stream() 流根据对象属性去重
    vue 当前端传回后端,后端使用实体类接收数据显示报错
    js 中 getMonth() 获取的月份比现实少一个月
    vue连个数组对比
    JS链接跳转方法
    ElementUI--表格toggleRowSelection无法选中
    Linux 常用命令
  • 原文地址:https://www.cnblogs.com/TheShadow/p/11370196.html
Copyright © 2011-2022 走看看