网络流24题

1/24 飞行员配对方案问题

题解:二分匹配

代码:

#include <iostream>
#include<bits/stdc++.h>
using namespace std;

const int maxn = 105;
int g[maxn][maxn];
int linker[maxn];
bool used[maxn];
int n,m;
bool dfs(int u){
    int v;
    for(v=0;v<n;v++){
        if(g[u][v]&&!used[v]){
            used[v]=true;
            if(linker[v]==-1||dfs(linker[v])){
                linker[v]=u;
                return true;
            }
        }
    }
    return false;
}
int hungary(){
    int res=0;
    memset(linker,-1,sizeof(linker));
    int u;
    for(u=0;u<m;u++){
        memset(used,false,sizeof(used));
        if(dfs(u))res++;
    }
    return res;
}
int main()
{
   // freopen("1.txt","r",stdin);
    int i;
    scanf("%d%d",&m,&n);
    int a,b;
    while(scanf("%d%d",&a,&b)){
        if(a+b==-2)break;
        a--,b--;
        g[a][b]=1;
    }
    printf("%d\n",hungary());
    for(i=0;i<n;i++){
        if(linker[i]!=-1)printf("%d %d\n",linker[i]+1,i+1);
    }
    return 0;
}

2/24 餐巾计划问题

题解:关键是建边。注意纸巾的流入流出关系。将一天拆成上下午,存在的条件为:

上午流量必须大于给定值,上午可以买入任意多流量,下午流量为上午用的流量,可以回流到洗完的天数,纸巾可以洗好留着不用(或者说可以不洗)。

所以要建种边:

1.早上流向汇点,容量need[i],费用0;

2.源点流向早上,容量inf,费用p。

3.源点流向晚上,容量need[i],费用0(早上用过的纸巾)

4.晚上流向晚上,容量inf,费用0(今夜不洗留到明夜)

5.6.今晚留到m天后的早上,容量inf,花费m or n。

代码:

#include <iostream>
#include<cstdio>
#include<cstring>
#include<queue>

using namespace std;
typedef long long ll;
const int maxn=4005;
const int maxm=29005;
const long long  inf=0x3f3f3f3f;
struct Edge{
	int to,next;
	ll cap,flow,cost;
}edge[maxm];
int tol;
int head[maxn];
int pre[maxn];
ll dis[maxn];
bool vis[maxn];
int N;
void init(int n){
	N=n;
	tol=0;
	memset(head,-1,sizeof(head));
}
void addedge(int u,int v,long long  cap,long long  cost){
	edge[tol].to=v;
	edge[tol].cost=cost;
	edge[tol].cap=cap;
	edge[tol].flow=0;
	edge[tol].next=head[u];
	head[u]=tol++;
	edge[tol].to=u;
	edge[tol].cost=-cost;
	edge[tol].cap=0;
	edge[tol].flow=0;
	edge[tol].next=head[v];
	head[v]=tol++;
}
bool spfa(int s,int t){
	queue<int>q;
	int i;
	for(i=0;i<N;i++){
		dis[i]=inf;
		vis[i]=false;
		pre[i]=-1;
	}
	dis[s]=0;
	vis[s]=true;
	q.push(s);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=false;
		for(i=head[u];i!=-1;i=edge[i].next){
			int v=edge[i].to;
			if(edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost){
				dis[v]=dis[u]+edge[i].cost;
				pre[v]=i;
				if(!vis[v]){
					vis[v]=true;
					q.push(v);
				}
			}
		}
	}
	if(pre[t]==-1)return false;
	else return true;
}
int mincostmaxflow(int s,int t,long long  &cost){
	long long  flow=0;
	cost=0;
	while(spfa(s,t)){
		long long  Min=inf;
		int i;
		for(i=pre[t];i!=-1;i=pre[edge[i^1].to]){
			if(Min>edge[i].cap-edge[i].flow)Min=edge[i].cap-edge[i].flow;
		}
		for(i=pre[t];i!=-1;i=pre[edge[i^1].to]){
			edge[i].flow+=Min;
			edge[i^1].flow-=Min;
			cost+=edge[i].cost*Min;
		}
		flow+=Min;
	}
	return flow;
}
//////////
long long need[maxn];
int p,m,f,mm,s;
int main()
{
	int  n;
	int i;
	scanf("%d",&n);
	init(2*n+2);
	for(i=1;i<=n;i++){
		scanf("%lld",&need[i]);
	}
	scanf("%d%d%d%d%d",&p,&m,&f,&mm,&s);
	for(i=1;i<=n;i++){
		addedge(0,i,inf,p);
		addedge(0,n+i,need[i],0);
		addedge(i,2*n+1,need[i],0);
		if(i+m<=n)addedge(n+i,i+m,inf,f);
		if(i+mm<=n)addedge(n+i,i+mm,inf,s);
		if(i+1<=n)addedge(n+i,n+i+1,inf,0);
	}
	long long cost;
	mincostmaxflow(0,2*n+1,cost);
	printf("%lld\n",cost);
    //cout << "Hello world!" << endl;
    return 0;
}

3/24  家园 

题解:枚举答案,每次都在上一个答案的残量网络上跑网络流,用并查集检查能否到达,注意初始化并查集要初始化到n+2,否则会在第八个点出问题。

代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXN=20005;//点数的最大值
const int MAXM=1000005;//边数的最大值

struct Node
{
    int from,to,next;
    int cap;
}edge[MAXM];
int tol;

int dep[MAXN];//dep为点的层次
int head[MAXN];
int n;
int p[25];
void init()
{
    tol=0;
    memset(head,-1,sizeof(head));
    int i;
    for(i=1;i<=n+3;i++)p[i]=i;//new
}
void addedge(int u,int v,int w)//第一条变下标必须为偶数
{
    edge[tol].from=u;
    edge[tol].to=v;
    edge[tol].cap=w;
    edge[tol].next=head[u];
    head[u]=tol++;
    edge[tol].from=v;
    edge[tol].to=u;
    edge[tol].cap=0;
    edge[tol].next=head[v];
    head[v]=tol++;
}

int BFS(int start,int end)
{
    int que[MAXN];
    int front,rear;
    front=rear=0;
    memset(dep,-1,sizeof(dep));
    que[rear++]=start;
    dep[start]=0;
    while(front!=rear)
    {
        int u=que[front++];
        if(front==MAXN)front=0;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(edge[i].cap>0&&dep[v]==-1)
            {
                dep[v]=dep[u]+1;
                que[rear++]=v;
                if(rear>=MAXN)rear=0;
                if(v==end)return 1;
            }
        }
    }
    return 0;
}
int dinic(int start,int end)
{
    int res=0;
    int top;
    int stack[MAXN];//stack为栈,存储当前增广路
    int cur[MAXN];//存储当前点的后继
    while(BFS(start,end))
    {
        memcpy(cur,head,sizeof(head));
        int u=start;
        top=0;
        while(1)
        {
            if(u==end)
            {
                int min=INF;
                int loc;
                for(int i=0;i<top;i++)
                  if(min>edge[stack[i]].cap)
                  {
                      min=edge[stack[i]].cap;
                      loc=i;
                  }
                for(int i=0;i<top;i++)
                {
                    edge[stack[i]].cap-=min;
                    edge[stack[i]^1].cap+=min;
                }
                res+=min;
                top=loc;
                u=edge[stack[top]].from;
            }
            for(int i=cur[u];i!=-1;cur[u]=i=edge[i].next)
              if(edge[i].cap!=0&&dep[u]+1==dep[edge[i].to])
                 break;
            if(cur[u]!=-1)
            {
                stack[top++]=cur[u];
                u=edge[cur[u]].to;
            }
            else
            {
                if(top==0)break;
                dep[u]=-1;
                u=edge[stack[--top]].from;
            }
        }
    }
    return res;
}
/////////////////////
int m,k;
int cap[25],num[25],g[25][25];
int get(int ans,int x){
    if(x==n+2)return 20000;
    return ans*(n+1)+x;
}

int finde(int x){
    return x==p[x]?x:p[x]=finde(p[x]);
}
void join(int x,int y){
    x=finde(x);
    y=finde(y);
    if(x==y)return ;
    p[x]=y;
    return ;
}
int main()
{
    int i,j;
    scanf("%d%d%d",&n,&m,&k);
    init();
    for(i=0;i<m;i++)
    {
        scanf("%d%d",&cap[i],&num[i]);
        for(j=0;j<num[i];j++)
        {
            scanf("%d",&g[i][j]);
            if(g[i][j]==-1)g[i][j]=n+2;//moon
            else if(g[i][j]==0)g[i][j]=n+1;//earth
            if(j>0)join(g[i][j],g[i][j-1]);
        }

    }
    if(finde(n+1)!=finde(n+2)){
        printf("0\n");
        return 0;
    }

    int ans=0,flow=0,x,y;
    int s=0,t=20000;
    for(ans=1;flow<k;ans++){
        if(ans>1)
        {
            for(i=1;i<=n+1;i++)addedge(get(ans-1,i),get(ans,i),INF);
        }
        addedge(s,get(ans-1,n+1),INF);
        for(i=0;i<m;i++){
            x=(ans-1)%num[i];
            y=ans%num[i];
            addedge(get(ans-1,g[i][x]),get(ans,g[i][y]),cap[i]);
        }
        flow+=dinic(s,t);
    }
    printf("%d\n",ans-1);
        return 0;
}

代码:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值