【传送门:BZOJ1930】
简要题意:
给出n个豆子和它们的坐标,保证在原点的右上方,有两个PACMAN在原点的左下方,每个PACMAN只能往右或往上移动,每移动到一个豆子所在的位置,就会把豆子吃掉,求出两个PACMAN的路径不相交的情况下,总共能吃到最多豆子
题解:
费用流
建边:
st->S,T->ed流量都为2,费用都为2,这样子才能保证是两个PACMAN在吃
S->i流量为1,费用为0,i->i+n流量为1,费用为1,i+n->ed流量为1,费用为0
如果i能走到j,说明x[i]<=x[j]&&y[i]<=y[j],那么i+n->j流量为1,费用为0
然后跑最大费用最大流
然而T了,因为有很多多余的情况
首先如果i能走到j,j能走到k的话,那么实际上是不用连i->k的
但是如果单纯不连的话,会错,因为实际上j只能走一次,走完之后就不能从i走到k了(而实际上走了j,i依然能走到k)
所以多建一条边i->i+n流量为1,费用为0,这样子就可以使得i成为一次中转站且不影响答案
然后要把i+n->j流量变为2,因为有可能出现借道(也就是中转站)的情况
注意要多路增广
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; struct node { int x,y,d,c,next,other; }a[2100000];int len,last[5100]; void ins(int x,int y,int d,int c) { int k1=++len,k2=++len; a[k1].x=x;a[k1].y=y;a[k1].d=d;a[k1].c=c; a[k1].next=last[x];last[x]=k1; a[k2].x=y;a[k2].y=x;a[k2].d=-d;a[k2].c=0; a[k2].next=last[y];last[y]=k2; a[k1].other=k2; a[k2].other=k1; } int d[5100],list[5100],st,ed; int pre[5100],pos[5100]; bool v[5100]; bool spfa() { memset(d,-63,sizeof(d));d[st]=0; memset(v,false,sizeof(v));v[st]=true; list[1]=st; int head=1,tail=2; bool bk=false; while(head!=tail) { int x=list[head]; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(a[k].c>0&&d[y]<d[x]+a[k].d) { pre[y]=k; pos[y]=x; d[y]=d[x]+a[k].d; if(v[y]==false) { v[y]=true; if(y==ed) bk=true; list[tail++]=y;if(tail==ed+1) tail=1; } } } v[x]=false; head++;if(head==ed+1) head=1; } return bk; } int ans; void Flow() { while(spfa()) { int t=1<<31-1; for(int x=ed;x!=st;x=pos[x]) t=min(t,a[pre[x]].c); for(int x=ed;x!=st;x=pos[x]) { a[pre[x]].c-=t; a[a[pre[x]].other].c+=t; } ans+=t*d[ed]; } } struct zb { int x,y; }p[2100]; bool cmp(zb n1,zb n2) { if(n1.x<n2.x) return true; if(n1.x>n2.x) return false; if(n1.y<n2.y) return true; if(n1.y<n2.y) return false; return false; } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&p[i].x,&p[i].y); int S=0,T=2*n+1;st=T+1;ed=st+1; len=0;memset(last,0,sizeof(last)); ins(st,S,0,2);ins(T,ed,0,2); sort(p+1,p+n+1,cmp); for(int i=1;i<=n;i++) { ins(S,i,0,1); ins(i+n,T,0,1); ins(i,i+n,1,1); ins(i,i+n,0,1); int mmax=0; for(int j=i-1;j>=1;j--) { if(p[i].y>=p[j].y&&p[j].y>mmax) { mmax=p[j].y; ins(j+n,i,0,2); } } } ans=0; Flow(); printf("%d\n",ans); return 0; }