• 二者取其一(初遇)_网络流


    二者取其一,就是一堆物品,放入两个集合内,放进不同的集合内就会有不同的收益(或代价),使其收益(代价)最大(最小)的一种问题

    通常这类问题,使用最小割定理解决。

    最小割,即割边集中权值之和最小的一个集合

    比如这道题[SHOI2007]善意的投票

    是一道这样类型的题。

    我们将(S)点设为同意睡觉的超级源点(T)设为不同意睡觉的点,同以对好朋友之间连一条容量为1的无向边,这样建图。我们求得一个最小割,就是答案的解了

    不过这里有一个与其他网络流不同的地方,就是朋友间为什么要建立无向边,而不是有向边呢?

    我们如此思考,假设a,b不统一意见。有两种决策,

    • 让a同意b

    • 让b同意a

    有两种情况,我们并不能准确确定割边在哪个地方,所以我们需要建出两种情况的边来。

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<queue>
    #include<cstring>
    using std::queue;
    using std::min;
    const int maxn=11000;
    int n,m;
    struct node
    {
        int p;
        long long f;
        int nxt;
    };
    int head[maxn],tail=-1;
    int dis[maxn];
    int cur[maxn];
    node line[maxn<<5];
    void add(int a,int b,long long c)
    {
        line[++tail].p=b;
        line[tail].f=c;
        line[tail].nxt=head[a];
        head[a]=tail;
    }
    bool bfs(int s,int t)
    {
        queue<int>q;
        memset(dis,0,sizeof(dis));
        dis[s]=1;
        q.push(s);
        while(!q.empty())
        {
            int pas=q.front();q.pop();
            for(int i=head[pas];i!=-1;i=line[i].nxt)
                if(!dis[line[i].p]&&line[i].f)
                {
                    dis[line[i].p]=dis[pas]+1;
                    q.push(line[i].p);
                }
        }
        return dis[t];
    }
    long long dfs(int now,int aim,long long flow)
    {
        long long res=0,f;
        if(now==aim||!flow)	return flow;
        for(int &i=cur[now];i!=-1;i=line[i].nxt)
            if(dis[line[i].p]==dis[now]+1&&(f=dfs(line[i].p,aim,min(flow,line[i].f))))
            {
                res+=f;
                flow-=f;
                line[i].f-=f;
                line[i^1].f+=f;
                if(!flow) break;
            }
        return res;
    }
    long long dinic(int s,int t)
    {
        long long res=0;
        while(bfs(s,t))
        {
            for(int i=0;i<=n+1;i++)
                cur[i]=head[i];
            res+=dfs(s,t,0x7fffffff);
        }
        return res;
    }
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d%d",&n,&m);
    	int a,b,c,d;
    	int ans=0;
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&a);
    		if(a)
    			add(0,i,1),add(i,0,0);
    		else
    			add(i,n+1,1),add(n+1,i,0);
    		ans+=1;
    	}
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d",&a,&b);
    		add(a,b,1);
    		add(b,a,1);
    	}
    	printf("%d",dinic(0,n+1));
    }
    
  • 相关阅读:
    linux驱动---等待队列、工作队列、Tasklets【转】
    Pinctrl子系统之一了解基础概念【转】
    Linux内存管理(最透彻的一篇)【转】
    linux驱动学习笔记---实现中断下半部以及驱动编写规范(七)【转】
    一些网址下载【转】
    Linux /proc/$pid部分内容详解【转】
    Linux kernel workqueue机制分析【转】
    Linux进程核心调度器之主调度器schedule--Linux进程的管理与调度(十九)【转】
    Linux Kernel PANIC(三)--Soft Panic/Oops调试及实例分析【转】
    Linux内核调试的方式以及工具集锦【转】
  • 原文地址:https://www.cnblogs.com/Lance1ot/p/9387604.html
Copyright © 2020-2023  润新知