线性规划与网络流24题

    本来很久之前就打算开始做这个专题,也没太多完整时间,现在集中做一下 http://220.166.52.162/oj/problemlist?p=8 这个oj上能评测

    建图是白色字体 全选就能看见。

   首先奉上我的模板,后面的题目都是用均采用这个模板。  


#include<stdio.h>
#include<memory.h>

#define INF 0x3f3f3f3f
#define maxn 1100
#define maxe 1000010
int gap[maxn],dis[maxn],pre[maxn],cur[maxn];
int size,n,head[maxn];

struct Node{
	int c,v,next;
	Node(){}
	Node(int _v,int _c,int _next):v(_v),c(_c),next(_next){}
}E[maxe];

int sap(int s,int t) {
	memset(dis,0,sizeof(dis));
	memset(gap,0,sizeof(gap));
	for(int i=1;i<=n;i++)	cur[i] = head[i];
	int u = pre[s] = s,maxflow = 0,aug = -1;
	gap[0] = n;
	while(dis[s] < n) {
loop:		for(int &i = cur[u]; i != -1; i = E[i].next) {
			int v = E[i].v;
			if(E[i].c && dis[u] == dis[v] + 1) {
				if(aug==-1 || aug>E[i].c)	aug=E[i].c;
				pre[v] = u;
				u = v;
				if(v == t) {
					maxflow += aug;
					for(u = pre[u];v != s;v = u,u = pre[u]) {
						E[cur[u]].c -= aug;
						E[cur[u]^1].c += aug;
					}
					aug = -1;
				}
				goto loop;
			}
		}
		int mindis = n;
		for(int i = head[u]; i != -1 ; i = E[i].next) {
			int v = E[i].v;
			if(E[i].c && mindis > dis[v]) {
				cur[u] = i;
				mindis = dis[v];
			}
		}
		if( (--gap[dis[u]]) == 0)	break;
		gap[ dis[u] = mindis+1 ] ++;
		u = pre[u];
	}
	return maxflow;
}

void add(int u,int v,int c,int cc = 0) {
	E[size] = Node(v,c,head[u]);
	head[u] = size++;
	E[size] = Node(u,cc,head[v]);
	head[v] = size++;
}
void init(){
	memset(head,-1,sizeof(head));
	size=0;
}


    第一题 飞行员配对方案问题

    建图:    二分图最大匹配   新建源汇点s和t,s到所有外籍飞行员连一条容量是1的边,所有英国飞行员到t连一条容量是1的边。

   代码当中只有建图部分。

int f,sum;
int edge,uu[5100],vv[5100];

int main()
{
	int s,t;
	int i,u,v;
	freopen("D:\\in.txt","r",stdin);
	scanf("%d%d",&f,&sum);
	init();
	edge=0;
	while(scanf("%d%d",&u,&v),u+v!=-2){
		add(u,v,1);
		uu[edge]=u;
		vv[edge]=v;
		edge++;
	}
	
	n=sum+2;
	s=n-1;
	t=n;
	
	for(i=1;i<=f;i++)
		add(s,i,1);
	for(i=f+1;i<=sum;i++)
		add(i,t,1);

	printf("%d\n",sap(s,t));
	for(i=0;i<edge;i++)
		if(0 == E[2*i].c)
			printf("%d %d\n",uu[i],vv[i]);
	return 0;
}



 太空飞行计划问题

 建图:最大权闭合子图,正点权的点全部从s到该点建一条流量是权值的边,负点权的点全部从该点建一条容量是绝对值的边到t,其他的边容量变为无穷大。

对于输出解,从s点开始在残留网络里面染色,确定割集,这个割集就代表了一个选择方案。


他们oj一直上不去 更新先到这里吧 等以后oj能评测再说。或者考虑自己写一个spj。


第三题 最小路径覆盖问题

 建图: DAG的最小路径覆盖问题。 每个点拆成两个点x和x',对于一条A->B,那么就建成,a->b',然后跑二分图最大匹配就好。


第四题 魔术球问题

 建图:跟上题一样,也是一道DAG的最小路径覆盖问题,首先二分能放置的球的数目,然后如果判定n个柱子能否放下x个球,把每个球看成一个顶点,对于任意两个球,如果他们相加为平方数,那么就从小的到大的那个数连一条边,然后就构成了DAG,然后求最小路径覆盖,就是需要的柱子数目。


第五题 圆桌问题

 建图: 裸的最大流,将每个单位和每个餐厅都建成一个顶点,从每个单位连接到每个餐厅一条容量是1的边,代表这个单位可以派一个人去某个餐厅吃饭,然后从s点到每个单位连接一条容量是单位人数的边,作为限制,同样的每个餐厅连接一条容量是餐厅用餐人数的边到t,跑s到t之间的最大流,如果能跑到人数之和,就是满流,那么就有解,否则就是无解。


第六题最长递增子序列问题

 建图:应用分层图的思想,是的网络流跑出来的路径均是符合我们想法的。

    第一问 先做一次dp,同时记录下到每个点最长递增是多少。

    第二问,然后通过这个分层图建图,每个元素拆成两个顶点,i.a和i.b,建立从i.a->i.b容量是1的边, 作为约束条件,每个顶点只能被使用一次,然后s到所有的第一层的i.a建立容量是1的边,同样,最高层的i.b建立到t的容量是1的边,这样跑出来之后就是答案。

    第三问,因为我们这里第一个和最后一个可以使用多次,所以就撤销掉他们的约束条件。将(s,i.a),(i.a,i.b), (n.a,n.b) (n.b,t) 这四条边修改为最大,当然如果不存在就不用修改了。


第七题 试题库问题

 建图:裸的最大流,将每道题目和每个类别建成一个顶点,然后从每个题目连一条容量是1的边到它属于的类别(到每个类别连一条),然后从s到每个题目连一条容量是1的边,从每个类别连一条容量是需要题目数的边,那么跑一次s到t的最大流就是答案。


第八题 机器人路径规划问题

 建图:


第九题 方格取数问题

 建图: 先假设所有点我们都已经全部选择了,现在我们要舍弃一些点,使得任意我们选取的两个点之间不相邻,将整个图奇偶染色, 从每个奇点连一条容量是无穷大的边到偶点,然后从s到奇点连接一条容量是数字的边,同样从偶点连接一条容量是数字的边到t,然后从s到t跑一次最大流,解的输出还是老方法采用染色法找一个割集。


第十题 餐巾计划问题

 建图:非常好的一道题目,想了很久,刚开始的第一感觉就是费用流,然后就是每天的餐巾来源有3个,一个就是今天新买的,还有今天快洗出来的,第三种今天慢洗出来的,那么最终这个流就是流向了t,那么我们这里就存在一个问题,一个流结束在t点之后,相当于今天的毛巾就消失了,但实际上,这些毛巾只是变成了脏毛巾,还能再用,没做,我们可以每天手动补上这么多的脏毛巾,反正每天产生的脏毛巾数是确定的,构图不再是梦想。

        将每天拆成二倍点,i.a 和i.b,i.a理解为脏毛巾,i.b理解为干净毛巾,然后从s到i.a 连接一条容量是当天需求量的边,费用0,代表每天可以免费得到这么多脏毛巾,i.b连接一条容量是当天需求量费用流的边到t,意味着每天需要消耗这么多的新毛巾,i.a连接一条到i+1.a 的容量无穷大,费用0 的边,那么就意味着当天的脏毛巾可以免费留到第二天再去洗,然后就是i.a 到i+d1.b 容量无穷, 费用c1的边,代表快洗,i.a 到i+d2.b 容量无穷, 费用c2的边,代表慢洗。跑一次最小费用流就可以了。

		for(i=1;i<=N;i++)
			scanf("%d",need+i);
		init();
		
		n=2*N+2;
		s=n-1;
		t=n;
		
		for(i=1;i<=N;i++){
			add(s,i+N,INF,p);
			add(i+N,t,need[i],0);
			if(i+d1 <= N)
				add(i,i+d1+N,INF,c1);
			if(i+d2 <= N)
				add(i,i+d2+N,INF,c2);
		}
		for(i=1;i<=N;i++){
			add(s,i,need[i],0);
			if(1 != i)
				add(i-1,i,INF,0);
		}
		mcmf(s,t);
		printf("%d\n",mincost);



第十一题 航空路线问题

 建图:比较简单的费用流,因为有每个城市只能访问一次的约束条件,所以先将城市拆点,设最西面的点位S,最东面的点为T,对于每个点i.a->i.b建立一条容量是1费用是0的边,特别的对于S和T两个点,s.a->s.b和t.a->t.b 建立容量是2,费用为0的边,对于原图中的每条航线,u->v,(u在v的西面),那么我们就建立从u.b->v.a 和 边,容量是1,费用是花费,最后跑一次从s.a 到 t.b 的最小费用最大流,如果满流不到2 那么就是无解,否则费用就是最小代价。


第十二题 软件补丁问题

 建图:状压完100万个状态,状态之间有转移,同时所有的时间都是正的,直接最短路搞起,spfa,dijkstra任君选择。



第十三题 星际转移问题

 建图:  满流判定+二分

        还是分层图,分层图用处多多,有一些情况下我们无法让网络流跑出符合我们要求的流出来,可能会违反规则,分层图就派上用场啦,使得跑出来的都是在我们规划下的路径,就像在这道题当中,因为飞船是按照一定的规则来运动,而且所有的单位时间都是一样的,直接建成分层图, 如果a->b 那么就是a和下一层的b相连,容量是限制飞船容量的。而且人可以停在某个空间站不懂,所以就是该层a到下一层的a建立一条容量是无穷的边,建立一条s到第一层的地球容量是人数的边,同时最后一层的月球到t连一条容量无穷得边,这样满流就是可以在规定时间内转移完毕。


第十四题 孤岛营救问题

 建图:   总共有P了类钥匙,那么就状压之后成为2^P个状态,代表钥匙取到的情况,然后直接扩展状态跑最短路就好。



第十五题 汽车加油行驶问题

 建图: 跟上一道一样,仔细观察发现,其实题目中的K是小于10的,所以我们直接扩展状态,代表到某点还有多少油作为一个状态直接跑最短路。



第十六题 数字梯形问题

 建图:

规则1,直接从起点连接m条容量是1的边到第一排的点,然后之后把所有的点拆成二倍点,加上容量限制1,然后每个点到下面一排的两个相邻的点,加上一条容量是1的边就好。

规则2,把容量限制去掉,不用拆点,然后点之间的连边还是容量1。

规则3,因为不限制路径,所以依旧不需要去拆点,而且点之间的连边容量INF,但是起点到第一排点连接m条容量是1的边就好。



第十七题 运输问题

 建图:直接建成二部图,然后在两排点之间连的边就是他们的费用,然后分别跑一次最小费用流和最大费用流,就是两种情况下的答案。



第十八题 分配问题

 建图: 建立二部图模型,然后在之间建边,分别进行最小费用流和最大费用流。


第十九题 负载平衡问题

 建图:每个仓库拆成两个点,S到X连接一条容量是原有存货量的边,X到X‘连接一条容量是无穷,费用为0的边,同时表示转移,X’连接一条容量无穷,费用为1的边到周围两个点Y,那么每个X‘连接一条容量是平均值,费用为0的边,跑一次最小费用流。


第二十题 深海机器人问题

 建图: 因为这道题目的限制是说每条的价值只能获得一次,所以说我们这里将边进行分类加两条,图中的每个网格点看成网络图中的顶点,然后每两个点之间之间加一条容量是1,费用是价值的边,还有一条容量是无穷费用为零的边,然后每个起始点和终点加好对应的边,跑一次最大费用流。


第二十一题 最长k可重区间集问题

 建图:第一种,离散化,在每两个相邻点之间建立一条容量是K,费用为相邻长度的边,然后对于每一个区间,从S建立一条容量是1费用为0的边到入口处,出口处建立一条容量是1,费用为0的边到T.最后加一条容量是无穷,费用为0的边从S到T.用来保证选取到最优解而不是流最大解。

第二种,离散化,在每相邻两个点之间建立一条容量是无穷,费用为0的边,然后对于每个区间,建立一条容量是1,费用是长度的边,代表选择了这条边,然后在最左边的起点处加上一条容量是K,费用为0的边,在最右边的出口处加上一条容量是K,费用是0的边,然后在S和T之间跑一次最大费用流就可以。



第二十二题 最长k可重线段集问题

 建图: 只能采用上一题中的第二种建图方法,方法一致。



第二十三题 火星探险问题

 建图:将图中的所有点拆成二倍点,然后从i.b连接一条容量是无穷的边到南方和东方的两个点.a ,然后对于所有的岩石从i.a到 i.b 连接一条容量是1,费用是1的边,加上一条容量无线,费用为0的边,对于所有的空地,连接一条容量为无穷,费用为0的边。

然后从起点到终点跑一次最大费用流。(应该如何去输出路径)



第二十四题 骑士共存问题

 建图: 先奇偶染色,就变成了二部图,然后就是与方格取数一样的做法,每个奇点到相邻的偶点连接一条容量为无穷大的边,用来保证不能被割掉,然后s到奇点建立一条容量是1的点,偶点到t建一条容量是1的边,s到t最大流就是答案。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值