【LA3487】最小割-经典模型 两种方法

题目链接

题意:A、B两个公司要买一些资源(他们自己买的资源不会重复),一个资源只能卖给一个公司。问最大收益。

simple input 部分:

54 1 //买到1就给54元

15 2

33 3

2 4 5//买到4、5就给2元

题解:这道题是很经典的模型题,在这里给出两个方法。

方法一 把每个询问看成一个点,然后A的询问连源点,B的询问连汇点,如果AB间的某个询问有矛盾就在它们中间连一条无限大的边,ans=sum-最小割。

// 方法一 把每个询问看成一个点,然后A的询问连源点,B的询问连汇点,如果AB间的某个
// 询问有矛盾就在它们中间连一条无限大的边,ans=sum-最小割。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
#include<vector>
using namespace std;

const int N=300100,INF=(int)1e9;
int s,t,len,num;
int first[2*N],dis[2*N];
int A[N],B[N],p1[N],p2[N];
// bool vis[2*N];
bool vis[3100][3100];
struct node{
    int x,y,d,next;
}a[6*N];
queue<int> q;

int minn(int x,int y){return x<y ? x:y;}
int maxx(int x,int y){return x>y ? x:y;}

void ins(int x,int y,int d)
{
    a[++len].x=x;a[len].y=y;a[len].d=d;
    a[len].next=first[x];first[x]=len;
    a[++len].x=y;a[len].y=x;a[len].d=0;
    a[len].next=first[y];first[y]=len;
}

bool bfs(int st,int ed)
{
    while(!q.empty()) q.pop();
    memset(dis,-1,sizeof(dis));
    dis[st]=0;
    q.push(st);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=first[x];i!=-1;i=a[i].next)
        {
            int y=a[i].y;
            if(dis[y]==-1 && a[i].d>0)
            {
                dis[y]=dis[x]+1;
                q.push(y);
            }
        }
    }
    return (dis[ed]!=-1);
}

int dfs(int x,int ed,int flow)
{
    int r=0,p;
    if(x==ed) return flow;
    for(int i=first[x];i!=-1;i=a[i].next)
    {
        int y=a[i].y;
        if(dis[y]==dis[x]+1 && a[i].d>0)
        {
            p=minn(a[i].d,flow-r);
            p=dfs(y,ed,p);
            r+=p;
            a[i].d-=p;
            a[i^1].d+=p;
        }
    }
    if(!r) dis[x]=-1;
    return r;
}

int dinic(int st,int ed)
{
    int ans=0;
    while(bfs(st,ed))
    {
        int p;
        while(p=dfs(st,ed,INF)) ans+=p;
    }
    return ans;
}

int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        len=-1;
        memset(first,-1,sizeof(first));
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
        memset(vis,0,sizeof(vis));
        int n,m,sum=0,mx=0,num=300001;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&p1[i]);
            sum+=p1[i];
            int x;num++;
            while(1)
            {
                char c;
                scanf("%d%c",&x,&c);
                A[x]=i;
                mx=maxx(mx,x);
                if(c==‘\n‘) break;
            }
        }
        scanf("%d",&m);
        s=0,t=n+m+1;
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&p2[i]);
            sum+=p2[i];num++;
            int x;
            while(1)
            {
                char c;
                scanf("%d%c",&x,&c);
                B[x]=i;
                mx=maxx(mx,x);
                if(c==‘\n‘) break;
            }
        }
        for(int i=1;i<=n;i++) ins(s,i,p1[i]);
        for(int i=1;i<=m;i++) ins(i+n,t,p2[i]);
        for(int i=1;i<=mx;i++)
        {
            if(!A[i]||!B[i]||vis[A[i]][B[i]]) continue;
            vis[A[i]][B[i]]=true;
            ins(A[i],B[i]+n,INF);
        }
        printf("Case %d:\n",++cas);
        printf("%d\n",sum-dinic(s,t));
        if(T) printf("\n");
    }
    return 0;
}

方法一

方法二:对于每个询问,新建一个点x,如果是A就源点连向这个点,流量为价钱p,然后x连向这个询问所要求买的资源c[i],流量为INF。

如果是B则反过来,连向汇点。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
#include<vector>
using namespace std;

const int N=300100,INF=(int)1e9;
int s,t,len,num;
int first[2*N],dis[2*N];
int A[N],B[N];
bool vis[3100][3100];
struct node{
    int x,y,d,next;
}a[6*N];
queue<int> q;

int minn(int x,int y){return x<y ? x:y;}
int maxx(int x,int y){return x>y ? x:y;}

void ins(int x,int y,int d)
{
    a[++len].x=x;a[len].y=y;a[len].d=d;
    a[len].next=first[x];first[x]=len;
    a[++len].x=y;a[len].y=x;a[len].d=0;
    a[len].next=first[y];first[y]=len;
}

bool bfs(int st,int ed)
{
    while(!q.empty()) q.pop();
    memset(dis,-1,sizeof(dis));
    dis[st]=0;
    q.push(st);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=first[x];i!=-1;i=a[i].next)
        {
            int y=a[i].y;
            if(dis[y]==-1 && a[i].d>0)
            {
                dis[y]=dis[x]+1;
                q.push(y);
            }
        }
    }
    return (dis[ed]!=-1);
}

int dfs(int x,int ed,int flow)
{
    int r=0,p;
    if(x==ed) return flow;
    for(int i=first[x];i!=-1;i=a[i].next)
    {
        int y=a[i].y;
        if(dis[y]==dis[x]+1 && a[i].d>0)
        {
            p=minn(a[i].d,flow-r);
            p=dfs(y,ed,p);
            r+=p;
            a[i].d-=p;
            a[i^1].d+=p;
        }
    }
    if(!r) dis[x]=-1;
    return r;
}

int dinic(int st,int ed)
{
    int ans=0;
    while(bfs(st,ed))
    {
        int p;
        while(p=dfs(st,ed,INF)) ans+=p;
    }
    return ans;
}

int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        len=-1;
        memset(first,-1,sizeof(first));
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
        memset(vis,0,sizeof(vis));
        int n,m,p,sum=0,mx=0,num=300001;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&p);
            sum+=p;
            int x;num++;
            ins(0,num,p);
            while(1)
            {
                char c;
                scanf("%d%c",&x,&c);
                ins(num,x+1,INF);
                if(c==‘\n‘) break;
            }
        }
        scanf("%d",&m);
        s=0,t=1;
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&p);
            sum+=p;num++;
            ins(num,t,p);
            int x;
            while(1)
            {
                char c;
                scanf("%d%c",&x,&c);
                ins(x+1,num,INF);
                if(c==‘\n‘) break;
            }
        }
        for(int i=1;i<=n;i++) ins(s,i,p1[i]);
        for(int i=1;i<=m;i++) ins(i+n,t,p2[i]);
        for(int i=1;i<=mx;i++)
        {
            if(!A[i]||!B[i]||vis[A[i]][B[i]]) continue;
            vis[A[i]][B[i]]=true;
            ins(A[i],B[i]+n,INF);
        }
        printf("Case %d:\n",++cas);
        printf("%d\n",sum-dinic(s,t));
        if(T) printf("\n");
    }
    return 0;
}

方法二

时间: 2024-08-12 22:48:18

【LA3487】最小割-经典模型 两种方法的相关文章

【BZOJ 3232】圈地游戏 二分+SPFA判环/最小割经典模型

最小割经典模型指的是“一堆元素进行选取,对于某个元素的取舍有代价或价值,对于某些对元素,选取后会有额外代价或价值”的经典最小割模型,建立倒三角进行最小割.这个二分是显然的,一开始我也是想到了最小割的那个模型的但是我觉得他会不是一个圈我就否掉了,但是仔细想想的话会发现,如果是这样的话所得到的答案一定小于等于一个圈的答案(浓度),所以我们可定会得到最终答案,所以这样做是可以的,所以说要有宽松得正解的意识(泥沙俱下但沙子不影响我泥).当时我否掉最小割以后就立马去想费用流了,然后想到建图后发现那样建图虽

清除SQLServer日志的两种方法

日志文件满而造成SQL数据库无法写入文件时,可用两种方法:一种方法:清空日志.1.打开查询分析器,输入命令DUMP TRANSACTION 数据库名 WITH NO_LOG2.再打开企业管理器--右键你要压缩的数据库--所有任务--收缩数据库--收缩文件--选择日志文件--在收缩方式里选择收缩至XXM,这里会给出一个允许收缩到的最小M数,直接输入这个数,确定就可以了. 另一种方法有一定的风险性,因为SQL SERVER的日志文件不是即时写入数据库主文件的,如处理不当,会造成数据的损失.1: 删除

SPOJ 839 Optimal Marks 最小割 经典 按位建图

胡伯涛论文中的一题,经典建模,由于二进制每一位异或不会相互影响,所以我们把问题转换模型,按位处理. 即已知一些点的标号0/1(还有些可以自己任意改),和一些边,边权定义为两端点标号的异或,要求边权和最小的标号方案. 我们联想到最小割求的是从源到汇容量最小的边权和. 建图: 标号为1的和源点相连,容量INF,标号为0的和汇点相连,容量INF,这些边是不能割掉的(这些点标号已经明确) 原图相连的边,连边,容量为1.(若将此边割掉,则两端点一个为0,一个为1,割为1) 跑完最大流后,在残量网络中dfs

[转]Delphi调用cmd的两种方法

delphi调用cmd的两种方法vars:string;begins:='cmd.exe /c '+edit1.Text+' >c:\1.txt';winexec(pchar(s),sw_hide);sleep(2000);memo1.Lines.LoadFromFile('c:\1.txt'); 2shellexecute(handle,nil,'cmd.exe',pchar(form2.edit1.text),nil,sw_hide);WinExec主要运行EXE文件.如:WinExec(’

旧文备份:利用一个定时器实现多个虚拟定时器的两种方法

固定周期法 使用一个硬件定时器进行固定周期(比如1ms)定时,用一个结构体数组作为软定时器描述表,数组的结构体数就是最大虚拟定时器的数量,每个结构体的成员都包括虚拟定时器状态(空闲.激活.运行.超时触发.周期触发).定时值(换算成定时周期数,例如1ms的硬件定时周期,现进行125ms的定时,定时值就是125).标识ID和回调函数等:用一个变量作为定时周期计数器,每次进入定时中断,重置定时器,扫描结构体数组中的每个成员结构体,对定时值做减一操作,然后判断该定时值是否为0,是则判定该值对应的虚拟定时

linux下日期时间自动同步设置(rdate,ntpdate两种方法)

linux下同步时间,至少有两种方法:rdate,ntpdate两种.centos最小化安装默认不安装,先确认已经安装过,否则先安装.其中rdate本身是用来获取远程时间服务器上时间用的,带上 -s 参数,就可以将获取到的时间应用到本地系统. NAME       rdate - get the time via the networkSYNOPSIS       rdate [-p] [-s] [-u] [-l] [-t sec] [host...]DESCRIPTION       rdat

WebGL中添加天空盒的两种方法

天空盒 的添加可以让模型所在的场景非常漂亮,而其原理也是非常简单的,相信看完下面代码就可以明白了. 说到天空盒的两种方法,倒不如说是两种写法,分别用了纹理加载的两个方法:loadTexture和loadTextureCube. 特别注意:图片的顺序 [方法一] var imagePrefix = "images/"; var directions = ["posx", "negx", "posy", "negy&qu

登录用到了两种方法GET和POST方法 用reqeusts实现

这里需要补充的是,客户端不仅仅是一些简单的操作,它也是会处理一些运算,业务逻辑的处理等.也就是说,客户端也做着一些本该由服务器来做的一些事情 这个错误第一反应是是不是新的这个gateway节点有问题啊?但是想想这是不可能的,因为并不是所有请求都不可以,而且找了gateway部门的同事问了下,也不存在对请求单独处理的逻辑. 认证的同时要抓取页面表单的其他input标签的name和value.joomla的较为简单,网站一般不会明文传输用户名和密码,遇到这种情况需要分析引入的js文件,模拟加密算法.

Android_Timer与Runnable两种方法的定时器

先理清概念: Timer是一个执行任务的类,他接受TimerTask的参数; Timer执行任务有两种方式,一种是在某个指定的时候执行某个任务timer.schedule(Timertask task,Data data),另一种是在多长时间之后执行某个任务timer.schedule(TimerTask task,long delay): TimerTask中执行指定的任务,但由于Android是线程安全的,所有的界面的更新都必须放到主线程中,所以要使用到Handler对象. Android的