[NOI2015]小园丁与老司机(DP+上下界最小流)
2018-08-07 13:38
477 查看
由于每行点的个数不超过1000,所以行内DP可以使用$O(n^2)$算法。
先找到每个点所能直接到达的所有点(x,y,x+y或x-y相同),用排序实现。
第一问:以行为阶段,对于每行,暴力枚举最有路径在这行上的起点和终点,g[x]记录当这行的最优路径以x为起点时,终点应在什么位置,f[x]记录走到x且这一行以x为起点,之后最多还能走到多少个点。
第二问:由于当一行的起点和终点都确定后,决策也是确定的,故只需要沿着DFS一遍即可得到最优路径。
第三问:先考虑怎么找到所有可能在最优路径上的边,同样暴力枚举每行的起点和终点即可,注意处理(0,0)的情况。找到符合要求的边后,问题就是用最少的路径数覆盖这些边,使用有上下界的最小流求解。
#include<map> #include<cstdio> #include<algorithm> #define rep(i,l,r) for (int i=(l); i<=(r); i++) #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) using namespace std; const int N=50010,M=1000010,inf=1000000000; bool va ,vb ; int n,mx,cnt=1,s0,ss,tt,S,T,d ,f ,h ,dis ,idx ,q ,cur ,a ,mp [6]; int Lst ,Nxt ,pre ,suf ,premx ,sufmx ,to[M],fl[M],nxt[M],Do ,Do2 ; struct P{ int x,y; }p ; bool cmp1(int a,int b){ return (p[a].y==p[b].y) ? p[a].x<p[b].x : p[a].y<p[b].y; } bool cmp2(int a,int b){ return (p[a].x==p[b].x) ? p[a].y<p[b].y : p[a].x<p[b].x; } bool cmp3(int a,int b){ return (p[a].x-p[a].y==p[b].x-p[b].y) ? p[a].y<p[b].y : p[a].x-p[a].y<p[b].x-p[b].y; } bool cmp4(int a,int b){ return (p[a].x+p[a].y==p[b].x+p[b].y) ? p[a].y<p[b].y : p[a].x+p[a].y<p[b].x+p[b].y; } bool cmp5(int a,int b){ return (p[a].y==p[b].y) ? p[a].x<p[b].x : p[a].y>p[b].y; } inline void add(int u,int v,int w){ to[++cnt]=v; fl[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt; to[++cnt]=u; fl[cnt]=0; nxt[cnt]=h[v]; h[v]=cnt; } void work(int u,int v,int low,int high){ add(u,v,high-low); d[u]-=low; d[v]+=low; } bool bfs(){ rep(i,0,n+6) dis[i]=0; dis[S]=1; q[1]=S; for (int st=0,ed=1; st<ed; ){ int x=q[++st]; For(i,x) if (fl[i] && !dis[k=to[i]]) dis[k]=dis[x]+1,q[++ed]=k; } return dis[T]; } int dfs(int x,int lim){ if (x==T) return lim; int c=0; for (int i=cur[x],k; i; i=nxt[i]) if (fl[i] && dis[k=to[i]]==dis[x]+1){ int t=dfs(k,min(lim-c,fl[i])); c+=t; fl[i]-=t; fl[i^1]+=t; if (fl[i]) cur[x]=i; if (c==lim) return lim; } if (!c) dis[x]=-1; return c; } int dinic(){ int res=0; while (bfs()){ rep(i,1,n+6) cur[i]=h[i]; res+=dfs(S,inf); } return res; } void Print(int x){ if (!x) return; printf("%d ",x); if (Do[x]==0) { Print(mp[x][Do2[x]]); return; } if (idx[x]>idx[Do[x]]){ int t=Do[x]; for (int i=Nxt[x]; i; i=Nxt[i]) printf("%d ",i); for (x=Lst[x]; x!=t; x=Lst[x]) printf("%d ",x); printf("%d ",t); Print(mp[t][Do2[t]]); return; } if (idx[x]<idx[Do[x]]){ int t=Do[x]; for (int i=Lst[x]; i; i=Lst[i]) printf("%d ",i); for (x=Nxt[x]; x!=t; x=Nxt[x]) printf("%d ",x); printf("%d ",t); Print(mp[t][Do2[t]]); return; } } void solve1(){ rep(i,1,n) a[i]=i; sort(a+1,a+n+1,cmp1); rep(i,2,n) if (p[a[i-1]].y==p[a[i]].y) Lst[a[i]]=a[i-1],Nxt[a[i-1]]=a[i]; sort(a+1,a+n+1,cmp2); rep(i,2,n) if (p[a[i-1]].x==p[a[i]].x) mp[a[i-1]][1]=a[i]; sort(a+1,a+n+1,cmp3); rep(i,2,n) if (p[a[i-1]].x-p[a[i-1]].y==p[a[i]].x-p[a[i]].y) mp[a[i-1]][2]=a[i]; sort(a+1,a+n+1,cmp4); rep(i,2,n) if (p[a[i-1]].x+p[a[i-1]].y==p[a[i]].x+p[a[i]].y) mp[a[i-1]][3]=a[i]; sort(a+1,a+n+1,cmp5); rep(i,1,n) idx[a[i]]=i; } void W(int x){ rep(i,1,3) if (mp[x][i]){ int to=mp[x][i]; if (f[to]+1>f[x]) Do2[x]=i,f[x]=f[to]+1; } } int calc(int x,int y){ return (idx[x]<idx[y]) ? pre[y]+f[mp[y][Do2[y]]] : suf[y]+f[mp[y][Do2[y]]]; } bool jud(P &p){ return p.x==0 || p.x==p.y || p.x==-p.y; } void solve2(){ rep(k,1,n) if (p[a[k]].y!=p[a[k-1]].y) pre[a[k]]=1; else pre[a[k]]=pre[a[k-1]]+1; for (int k=n; k; k--) if (p[a[k]].y!=p[a[k+1]].y) suf[a[k]]=1; else suf[a[k]]=suf[a[k+1]]+1; rep(k,1,n) if (p[a[k]].y!=p[a[k-1]].y){ for (int x=a[k]; x; x=Nxt[x]) f[x]=1,W(x); for (int x=a[k]; x; x=Nxt[x]) for (int y=a[k]; y; y=Nxt[y]) if (y!=x && calc(x,y)>f[x]) f[x]=calc(x,y),Do[x]=y; } rep(i,1,n) if (jud(p[i])) mx=max(mx,f[i]); printf("%d\n",mx); rep(i,1,n) if (f[i]==mx && jud(p[i])) { Print(i); break; } puts(""); } int solve3(){ rep(i,1,n) if (f[i]==mx && jud(p[i])) work(s0,i,1,inf),va[i]=1; for (int i=n; i; i--) if (p[a[i]].y!=p[a[i+1]].y){ for (int x=a[i]; x; x=Lst[x]) if (va[x]){ if (f[mp[x][Do2[x]]]+1==f[x]) vb[x]=1; for (int y=a[i]; y; y=Lst[y]) if (y!=x && calc(x,y)==f[x]) vb[y]=1; } for (int x=a[i]; x; x=Lst[x]) if (vb[x]) rep(j,1,3) if (mp[x][j] && f[mp[x][j]]==f[mp[x][Do2[x]]]) va[mp[x][j]]=1,work(x,mp[x][j],1,inf); } rep(i,1,n+1) work(ss,i,0,inf),work(i,tt,0,inf); rep(i,1,n+1) if (d[i]>0) add(S,i,d[i]); else add(i,T,-d[i]); add(tt,ss,inf); dinic(); S=tt; T=ss; return inf-dinic(); } int main(){ freopen("driver.in","r",stdin); freopen("driver.out","w",stdout); scanf("%d",&n); s0=n+1; ss=n+2; tt=n+3; S=n+4; T=n+5; rep(i,1,n) scanf("%d%d",&p[i].x,&p[i].y); solve1(); solve2(); printf("%d\n",solve3()); return 0; }
相关文章推荐
- 【bzoj4200】[Noi2015]小园丁与老司机 STL-map+dp+有上下界最小流
- 【bzoj4200】[Noi2015]小园丁与老司机 dp+有上下界的网络流
- [DP 上下界最小流] BZOJ4200 [Noi2015]小园丁与老司机
- 【BZOJ4200】[Noi2015]小园丁与老司机 DP+最小流
- 4200: [Noi2015]小园丁与老司机 DP+有源汇上下界最小流
- bzoj4200 [Noi2015]小园丁与老司机(dp+记录路径+有源汇有上下界最小流)
- 【bzoj4200】【NOI2015】【小园丁与老司机】【dp+最小流】
- uoj132/BZOJ4200/洛谷P2304 [Noi2015]小园丁与老司机 【dp + 带上下界网络流】
- BZOJ4200 NOI2015小园丁与老司机(动态规划+上下界网络流)
- BZOJ4200: [Noi2015]小园丁与老司机 最小流
- [BZOJ]4200: [Noi2015]小园丁与老司机
- 4200: [Noi2015]小园丁与老司机
- bzoj4200 [NOI2015]小园丁与老司机
- [Noi2015]小园丁和老司机
- UOJ132 【NOI2015】小园丁与老司机
- NOI2015 小园丁与老司机
- 【NOI2015】小园丁与老司机 DP 网络流
- [BZOJ4200][Noi2015]小园丁与老司机
- 【NOI2015】小园丁与老司机
- BZOJ 4200 NOI 2015 小园丁与老司机