BZOJ 1930 吃豆豆(费用流)
首先这题的两条线不相交的限制可以去掉,因为如果相交的话把点换一换是不影响最终结果的。
剩下的费用流建图是显然的,把点拆为两个,建立超级源点s和源点ss汇点t,连边(s,ss,2,0). 对于每个点,连边(ss,i,1,0), (i,i',1,1),(i',t,1,0).
这样跑一遍费用流就行了,然而此题的边数可以达到n^2.无疑是OLE的。需要优化。
容易发现,对于点(i,j),(j,k),(i,k).如果这些点都可以互相到达的话,那么(i,k)这条边是不必要的。因为通过j到达k是不会比结果劣的。
所以启发我们将点按x坐标以第一关键字排序,y第二关键字排序。对于相同的列,选择大于前一列的y坐标且最小的点连边。
这样建边之后还存在一个问题,可能有个点两个吃豆人都需要经过,于是再建边(i,i',1,0),(i',j,2,0).
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi 3.1415926535 # define eps 1e-9 # define MOD 1000000007 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int res=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+(ch-'0'); return flag?-res:res; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=4005; //Code begin... struct Node{int x, y;}node[2005]; struct Edge{int to, next, cap, flow, cost;}edge[1000005]; int head[N], tol, pre[N], dis[N], nn; bool vis[N]; queue<int>q; void init(int n){nn=n; tol=0; mem(head,-1);} void addedge(int u, int v, int cap, int cost){ edge[tol].to=v; edge[tol].cap=cap; edge[tol].cost=cost; edge[tol].flow=0; edge[tol].next=head[u]; head[u]=tol++; edge[tol].to=u; edge[tol].cap=0; edge[tol].cost=-cost; edge[tol].flow=0; edge[tol].next=head[v]; head[v]=tol++; } bool spfa(int s, int t){ FO(i,0,nn) dis[i]=INF, vis[i]=false, pre[i]=-1; dis[s]=0; vis[s]=true; q.push(s); while (!q.empty()) { int u=q.front(); q.pop(); vis[u]=false; for (int i=head[u]; i!=-1; i=edge[i].next) { int v=edge[i].to; if (edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost) { dis[v]=dis[u]+edge[i].cost; pre[v]=i; if (!vis[v]) vis[v]=true, q.push(v); } } } if (pre[t]==-1) return false; else return true; } int minCostMaxflow(int s, int t, int &cost){ int flow=0; cost=0; while (spfa(s,t)) { int Min=INF; for (int i=pre[t]; i!=-1; i=pre[edge[i^1].to]) { if (Min>edge[i].cap-edge[i].flow) Min=edge[i].cap-edge[i].flow; } for (int i=pre[t]; i!=-1; i=pre[edge[i^1].to]) { edge[i].flow+=Min; edge[i^1].flow-=Min; cost+=edge[i].cost*Min; } flow+=Min; } return flow; } bool comp(Node a, Node b){ if (a.x==b.x) return a.y<b.y; else return a.x<b.x; } int main () { int n, s, ss, t; scanf("%d",&n); s=0; ss=2*n+1; t=2*n+2; init(t+1); addedge(s,ss,2,0); FOR(i,1,n) { scanf("%d%d",&node[i].x,&node[i].y); addedge(ss,i,1,0); addedge(i,n+i,1,-1); addedge(i,n+i,1,0); addedge(n+i,t,1,0); } sort(node+1,node+n+1,comp); FOR(i,1,n) { int mi=INF, now=0; FOR(j,i+1,n) { if (now==node[j].x||node[j].y<node[i].y) continue; if (node[j].y<mi) mi=node[j].y, now=node[j].x, addedge(n+i,j,2,0); } } int ans; minCostMaxflow(s,t,ans); printf("%d\n",-ans); return 0; }