【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
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
| 欢迎来原网站坐坐! >原文链接<