zoukankan      html  css  js  c++  java
  • 网络流 24 题 解题报告

    刚开始觉得 24 题都是板子,不屑于写题解,结果第 6 道就教我做人了……orz
    按照惯例,“机器人路径规划问题”是假题,不写。
    另外,网络流 24 题的全称是“网络流与线性规划 24 题”,所以里面有些题不需要网络流也可以解决,怎么简单怎么来。
    约定:用有序对 ((cap,cost)) 表示这条边的容量与费用。


    飞行员配对方案问题

    裸二分图匹配。

    软件补丁问题

    状压+最短路。水题。

    孤岛营救问题

    裸的 BFS。

    负载平衡问题

    环形均分纸牌,有贪心的结论,不展开说了。

    方格取数问题

    要求最大值,考虑用总和减去互斥最小值,套路地按坐标奇偶性建出二分图的流量网络,互斥的点之间建容量为 INF 的边,跑最小割即可。

    餐巾计划问题

    出师不利啊……才做了几题就趴下了……
    首先必须想到的是把一天拆成起始点和结束点两个,起始点用来接收新的纸巾,结束点用来决策脏纸巾的去向。
    一个显然的连边是 (S) 到每天的起始点连 ((infty,p)),代表买纸巾的操作。对于快(慢)洗,需要从 (i) 点的结束点向 (i+m)(或 (i+n))的起始点连边,这个也是比较简单的。
    然后是两个比较反直觉的连边:(S)(i) 的结束点连 ((r_i,0))(i) 的起始点向 (T)((r_i,0))
    感性理解一下,每天的终止点只会由源点与前一天的终止点连接,保证每天的纸巾处理量(也就是向以后留的纸巾)达到要求,起始点向汇点链接保证了每天的纸巾需求量达到要求。
    ps:如果想要不那么反直觉地做这道题,可以用上下界最小费用可行流做。
    这题还是得放放代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=2005,INF=0x3f3f3f3f;
    struct Edge{int to,nxt,c,w;}e[N*N>>1];
    int head[N<<1],cnt=1,n,r[N],p,d1,f,d2,s;
    int incf[N<<1],S,T,pre[N<<1],inq[N<<1],dis[N<<1];
    ll ans;
    queue<int> q;
    
    inline void add(int u,int v,int c,int w)
    {
    	e[++cnt]=(Edge){v,head[u],c,w};head[u]=cnt;
    	e[++cnt]=(Edge){u,head[v],0,-w};head[v]=cnt;
    }
    
    bool spfa()
    {
    	q.push(S);
    	for(int i=1;i<=T;++i) dis[i]=INF;
    	incf[S]=INF,dis[S]=0;
    	while(!q.empty())
    	{
    		int u=q.front(); inq[u]=0; q.pop();
    		for(int i=head[u],v;i;i=e[i].nxt)
    			if(e[i].c&&dis[v=e[i].to]>dis[u]+e[i].w)
    			{
    				dis[v]=dis[u]+e[i].w; pre[v]=i;
    				incf[v]=min(incf[u],e[i].c);
    				if(!inq[v]) inq[v]=1,q.push(v);
    			}
    	}
    	return dis[T]!=INF;
    }
    
    void upd()
    {
    	int x=T;
    	while(x!=S)
    	{
    		int i=pre[x];
    		e[i].c-=incf[T],e[i^1].c+=incf[T];
    		x=e[i^1].to;
    	}
    	ans+=1LL*dis[T]*incf[T];
    }
    
    int main()
    {
    	scanf("%d",&n); S=n*2+1; T=S+1;
    	for(int i=1;i<=n;++i)
    	{
    		scanf("%d",r+i);
    		add(S,i,r[i],0); add(i+n,T,r[i],0);
    	}
    	scanf("%d%d%d%d%d",&p,&d1,&f,&d2,&s);
    	for(int i=1;i<=n;++i)
    	{
    		add(S,i+n,INF,p);
    		if(i<n) add(i,i+1,INF,0);
    		if(i+d1<=n) add(i,i+n+d1,INF,f);
    		if(i+d2<=n) add(i,i+n+d2,INF,s);
    	}
    	while(spfa()) upd();
    	printf("%lld",ans);
    	return 0;
    }
    

    星际转移问题

    分层图最大流。
    按照时间分层建图。考虑从 0 枚举答案(天数),每增加一天就增加 (n+2) 个点,代表这一天的所有星球,然后连源点到地球、月球到汇点、每个星球的前一天到今天,容量均为 (infty);再按照每个飞船的移动路线连接这一天飞船该走的两个星球,容量为那个飞船的满载人数。注意,这些点与边都是直接建在之前的残量网络上的。最后直接跑最大流即可,若某一天的最大流大于等于 (k),直接输出答案;否则我们可以设置一个阙值(我设的是 500),大于这个天数还算不出就输出无解。评测链接

    最长 k 可重区间集问题

    考虑做如下转换,我们选 (k) 次,每次选权值尽量大的若干不相交的区间,这样做的正确性是显然的。
    用网络流体现选 (k) 次的过程,考虑如下建图:对每个点 (i)(i+1) 连边 ((k,0)),对于一个区间 ((l_i,r_i)) 左端点向右端点连边 ((1,r_i-l_i))(S) 连最左边的点,最右边的点连 (T),跑最大费用最大流即可。考虑费用流 EK 增广的过程,符合之前的转换。

    最长 k 可重线段集问题

    和上面的题做法一样,唯一的区别是有可能存在垂直于 (x) 轴的线段。解决方案是扩域,对每条区间的 ((x_1,x_2)) 变为 ((2x_1,2x_2)),这样如果 (x_1=x_2),那么我们连边 (2x_1 o 2x_2+1),否则连 (2x_1+1 o 2x_2)。正确性可以看这篇

    分配问题

    sb 题啊,行和列是二分图的两边,一个点就连对应行和列的两个点,费用就是权值,然后求个费用流就完了。

    运输问题

    和分配问题一样。

    数字梯形问题

    按照套路建图即可。三问对应的边权改改就行了。

    深海机器人问题

    拆点套路题……

    汽车加油行驶问题

    分层图最短路,果题。

    火星探险问题

    和深海机器人问题一模一样的套路。

    骑士共存问题

    ……草,这咋越来越水了啊……
    …………
    全部看完了,后面没有啥有趣的题了……所以……完结撒花……

  • 相关阅读:
    atitit.nfc 身份证 银行卡 芯片卡 解决方案 attilax总结
    atitit.php 流行框架 前三甲为:Laravel、Phalcon、Symfony2 attilax 总结
    Atitit.执行cmd 命令行 php
    Atitit. 图像处理jpg图片的压缩 清理垃圾图片 java版本
    atitit。企业组织与软件工程的策略 战略 趋势 原则 attilax 大总结
    atitit. 管理哲学 大毁灭 如何防止企业的自我毁灭
    Atitit.java的浏览器插件技术 Applet japplet attilax总结
    Atitit.jquery 版本新特性attilax总结
    Atitit. 软件开发中的管理哲学一个伟大的事业必然是过程导向为主 过程导向 vs 结果导向
    (转)获取手机的IMEI号
  • 原文地址:https://www.cnblogs.com/wzzyr24/p/13067489.html
Copyright © 2011-2022 走看看