zoukankan      html  css  js  c++  java
  • 【BZOJ1930】[Shoi2003]pacman 吃豆豆 最大费用最大流

    【BZOJ1930】[Shoi2003]pacman 吃豆豆

    Description

    两个PACMAN吃豆豆。一开始的时候,PACMAN都在坐标原点的左下方,豆豆都在右上方。PACMAN走到豆豆处就会吃掉它。PACMAN行走的路线很奇怪,只能向右走或者向上走,他们行走的路线不可以相交。 请你帮这两个PACMAN计算一下,他们俩加起来最多能吃掉多少豆豆。

    Input

    第一行为一个整数N,表示豆豆的数目。 接下来 N 行,每行一对正整数,表示第i个豆豆的坐标。任意两个豆豆的坐标都不会重合。

    Output

    仅有一行包含一个整数,即两个PACMAN加起来最多能吃掉的豆豆数量

    Sample Input

    8
    8 1
    1 5
    5 7
    2 2
    7 8
    4 6
    3 3
    6 4

    Sample Output

    7

    HINT

     

    N < = 2000

    题解:由于只需要跑两次,所以可以采用费用流。首先我们不用考虑两条路径相交的情况,因为我们可以将相交的部分交换一下,就不相交了。

    但是本题卡空间!!!所以有一个优化:如果a能到b,b能到c,则不需要连a到c的边。所以具体建边方法:

    1. S -> i 容量1,费用0
    2. i -> i' 容量1,费用1
    3. i' -> j 容量2,费用0 (注意容量是2!因为我们的优化中相当于是用a-b-c替换了a-c)
    4. i -> T 容量1,费用1

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    #include <algorithm>
    using namespace std;
    int n,cnt,sum,S,T,ans;
    int inq[4020],dis[4020],head[4020],to[4000010],next[4000010],cost[4000010],flow[4000010],pv[4020],pe[4020];
    struct node
    {
    	int x,y;
    }p[2010];
    queue<int> q;
    int bfs()
    {
    	memset(dis,0xc0,sizeof(dis));
    	dis[S]=0,q.push(S);
    	int i,u;
    	while(!q.empty())
    	{
    		u=q.front(),q.pop(),inq[u]=0;
    		for(i=head[u];i!=-1;i=next[i])	if(dis[to[i]]<dis[u]+cost[i]&&flow[i])
    		{
    			pv[to[i]]=u,pe[to[i]]=i,dis[to[i]]=dis[u]+cost[i];
    			if(!inq[to[i]])	inq[to[i]]=1,q.push(to[i]);
    		}
    	}
    	return dis[T]>0;
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    inline void add(int a,int b,int c,int d)
    {
    	to[cnt]=b,cost[cnt]=c,flow[cnt]=d,next[cnt]=head[a],head[a]=cnt++;
    	to[cnt]=a,cost[cnt]=-c,flow[cnt]=0,next[cnt]=head[b],head[b]=cnt++;
    }
    bool cmp(const node &a,const node &b)
    {
    	return (a.x==b.x)?(a.y<b.y):(a.x<b.x);
    }
    int main()
    {
    	n=rd(),S=0,T=2*n+1;
    	int i,j;
    	memset(head,-1,sizeof(head));
    	for(i=1;i<=n;i++)	add(S,i,0,1),add(i+n,T,0,1),add(i,i+n,1,1),add(i,i+n,0,1),p[i].x=rd(),p[i].y=rd();
    	sort(p+1,p+n+1,cmp);
    	for(i=1;i<=n;i++)
    	{
    		int mn=1<<30;
    		for(j=i+1;j<=n;j++)	if(p[j].y>=p[i].y&&p[j].y<mn)	add(i+n,j,0,2),mn=p[j].y;
    	}
    	while(bfs())
    	{
    		int mf=1<<30;
    		for(i=T;i!=S;i=pv[i])	mf=min(mf,flow[pe[i]]);
    		sum+=mf,ans+=dis[T]*mf;
    		if(sum==2)	break;
    		for(i=T;i!=S;i=pv[i])	flow[pe[i]]-=mf,flow[pe[i]^1]+=mf;
    	}
    	printf("%d",ans);
    	return 0;
    }//13 1 1 2 2 3 3 4 4 5 5 6 6 1 3 2 4 3 5 4 6 5 7 6 8 4 1
  • 相关阅读:
    Java swing 代码例子
    MySql
    swing 下拉菜单
    uiautomator2.0的配置的两种方法
    【Java】边框总结
    Java可视操作界面例子
    Java多线程例子
    使用Java让android手机自动执行重复重启
    形参的一种特殊写法
    this 基础使用方法
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7605233.html
Copyright © 2011-2022 走看看