本来很久之前就打算开始做这个专题,也没太多完整时间,现在集中做一下 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最大流就是答案。