zoukankan      html  css  js  c++  java
  • 【*篇】网络流24题——方格取数加强版

    将近一年没写(别问我为啥)网络流都忘光啦OvO现在决定来重拾一下...
    所以从哪道题开始哩?

    就决定是你了!

    "等等, 这不是个dp大水题么?"
    不好意思 放错链接了 应该是这个

    就决定是你了!

    Emmmm很明显唯一的区别就是把2改为了(k)... 可我们总不能跑一个(2k)维dp吧? 而且还不知道(k)是多少...
    那咋整呢...卖什么关子啊←_←标题都说是网络流了还用想别的?

    我们就考虑网络流吧... 网络流主要的问题就是建图啦~
    建图的时候就是考虑题目中的各个条件怎么转化为边的限制.
    可以把每个点拆成两个然后建边.

    • 在拆开的两个点之间建一条费用为(a[i][j]), 流量为1的边, 表示这个权值可以且仅可以取一次.
    • 再在拆开的两个点之间建一条费用为0, 流量为(infty)的边, 表示这个点可以走很多次, 但是走的后几次是取不到权值的.
    • 每个点的右边的点向右边或下边相邻的点的左边连一条费用为0, 流量为(infty)的边, 就是表示从这个点往下/往右走了啊
    • 从源点(S)向左上角点的左边连一条费用为0, 流量为(k)的边, 表示一共有(k)次要走.
    • 从汇点(T)向右下角点的右边连一条费用为0, 流量为(k)的边, 表示走完.
    • (不过好像源点和汇点连的边只要有一个流量是(k)起到限制作用, 另一个写(infty)或别的什么比(k)大的数也可以?

    以样例为例我们建出来的图应该长这样(由于从(S)出发只有一条边我就横过来了~~
    建图示例

    这样图就建好了, 然后我们跑一遍最大费用最大流就好了... 但是这玩意怎么跑呢?
    把每条边的费用设置成相反数然后跑最小费用最大流不就完了么←_← 最后结果是个负的再倒过来不就好了...
    (所以这种方法不能用来处理负权... 所以题目给的都是正整数...

    之后贴板子就好了... 不过再扯点别的...
    比如我忘记费用流怎么写了于是决定直接去学zkw费用流..
    之前也看过zkw(orz)的blog但是个人来讲并不是很喜欢里面的风格OvO
    然后baidu一下"zkw费用流"发现第一条是一位dalao改的zkw费用流,(讲的挺好的大家都去学就好了, 我就不发表自己的拙见了)我就直接按自己的码风改(chao)了一遍, 去刷了一下模板题... 本来昨天晚上想发出来的结果光顾着颓废了就耽搁了..
    那就不用发板子了直接跟着题贴出来就好咯...
    不过写起来和dinic是真的像..(正好复习了一下同样忘光的dinic..

    代码:

    #include <queue>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int N=5050;
    const int M=30050;
    const int INF=0x7f7f7f7f;
    inline int gn(int a=0,char c=0){
    	for(;c<'0'||c>'9';c=getchar());
    	for(;c>47&&c<58;c=getchar())a=a*10+c-48;return a;
    }
    struct edge{
    	int to,next,cost,flow;
    }e[M]; int v[N],tot=1;
    void buildedge(int x,int y,int cost,int flow){
    	e[++tot].to=y; e[tot].next=v[x]; v[x]=tot; e[tot].cost=cost; e[tot].flow=flow;
    	e[++tot].to=x; e[tot].next=v[y]; v[y]=tot; e[tot].cost=-cost; e[tot].flow=0;
    }
    int a[52][52],d[N],s,t,n,k,cost;
    bool vis[N];
    queue<int> q;
    inline bool spfa(const int& s,const int& t){
    	memset(vis,0,sizeof(vis));
    	memset(d,0x7f,sizeof(d));
    	vis[t]=1; d[t]=1; q.push(t);
    	while(!q.empty()){
    		int x=q.front(); q.pop(); vis[x]=0;
    		for(int i=v[x];i;i=e[i].next)
    			if(e[i^1].flow&&d[e[i].to]>d[x]-e[i].cost){
    				d[e[i].to]=d[x]-e[i].cost;
    				if(!vis[e[i].to]) 
    					q.push(e[i].to),vis[e[i].to]=1;
    			}
    	}
    	return d[s]<INF;
    }
    int dfs(int x,int mx,int s=0){ vis[x]=1;
    	if(x==t) return mx; int k;
    	for(int i=v[x];i;i=e[i].next)
    		if(!vis[e[i].to]&&e[i].flow&&d[e[i].to]==d[x]-e[i].cost){
    			k=::dfs(e[i].to, ::min(e[i].flow, mx-s));
    			if(k) cost+=k*e[i].cost,e[i].flow-=k,e[i^1].flow+=k,s+=k;
    			if(s==mx) break;
    		}
    	return s;
    }
    int mcmf(int flow=0){
    	while(::spfa(s, t)){ vis[t]=1;
    		while(vis[t]){
    			memset(vis,0,sizeof(vis));
    			flow+=dfs(s,INF);
    		}
    	} return flow;
    }
    int main(){ n=gn(),k=gn(); 
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=n;++j)
    			a[i][j]=gn();
    	int nn=n*n; s=0; t=nn<<1|1;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=n;++j){
    			int x=(i-1)*n+j;
    			::buildedge(x, x+nn, -a[i][j], 1);
    			::buildedge(x, x+nn, 0, INF);
    			if(i<n) ::buildedge(x+nn, x+n, 0, INF);
    			if(j<n) ::buildedge(x+nn, x+1, 0, INF);
    		}
    	::buildedge(s, 1, 0, k); ::buildedge(t-1, t, 0, k);
    	mcmf(); printf("%d",-cost);
    }
    

    结果就这么个昨天刚学的板子今天再写就写疵了.. mdzz..

    这个板子稍微改改可以水掉的题目(换句话说数据比较适合爆炸的程序对拍的:

    • luogu2405 (这个不用改2333
    • luogu1004 (把k=gn()改成k=2, 然后把读入一改就好
    • luogu1006 (还是k=2, 改改读入就行 这俩好像本来就是一个题吧?

    解释一下代码里面出现的过多的::是用来触发clang-complete的自动补全的...
    因为可以同时补全参数类型.. 超级好用的一个功能, 那么为什么不用呢XD
    所以就多出来很多::又懒得删了 反正也不会CE, 就这样呗OvO

  • 相关阅读:
    NSCoder
    OC_NSString、
    OC_内存管理(二)对象复制、循环引用问题、自动释放池
    OC_id类型
    OC_内存管理
    当 IDENTITY_INSERT 设置为 OFF 时,不能向表 '#TT' 中的标识列插入显式值。 sql server 临时表
    c# 访问SQL Server 其他会话正在使用事务的上下文
    EF 中事务的书写
    iis 不能访问json文件
    在开源中国(oschina)git中新建标签(tags)
  • 原文地址:https://www.cnblogs.com/enzymii/p/8412177.html
Copyright © 2011-2022 走看看