• 网络流 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 题啊,行和列是二分图的两边,一个点就连对应行和列的两个点,费用就是权值,然后求个费用流就完了。

    运输问题

    和分配问题一样。

    数字梯形问题

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

    深海机器人问题

    拆点套路题……

    汽车加油行驶问题

    分层图最短路,果题。

    火星探险问题

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

    骑士共存问题

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

  • 相关阅读:
    Sencha touch 2 入门 -------- DataView 显示服务器端JSON文件数据
    Sencha touch API
    Android Intent详解
    物流配送中商品订货数量的控制技术
    multiset基础学习,可以有重复类型的多重集合容器
    人生总会遇到浑噩期,但是需要反思
    创建Sencha touch第一个应用
    How I Turned Down $300,000 from Microsoft to go Full-Time on GitHub
    c++ list 合并操作函数实例
    电子设计与制作100例(第3版)
  • 原文地址:https://www.cnblogs.com/wzzyr24/p/13067489.html
Copyright © 2020-2023  润新知