BZOJ4200: [Noi2015]小园丁与老司机 最小流
来源:互联网 发布:国产电视剧推荐 知乎 编辑:程序博客网 时间:2024/05/08 08:53
题意:平面上有N(N<=50000)个点,从原点出发,每次可以走上、左、右、左上45度、右上45度五种方向,只有碰到一个未走过的点才能停止,求:
1.最多走多少点
2.输出一种方案
3.对于所有最优方案,只保留上、左上、右上的边,求最少条数的路径将所有边都覆盖
同一y坐标的点不超过1000个
前两问DP一下即可,将所有点按y为一关键字x为二关键字排序,然后对于每一行的每个点,枚举是从这行之前哪个点转移来,然后再n^2同行转移。同行转移的方案记录着实蛋疼了许久,感觉情况太多,结果突然想到其实只可能是一种方式——若从a走到同行的b,则先向b的反方向走到头,再走回b。于是就可以搞了。
第三问,这数据规模直接卡死了线性规划的内存,只好去学最小流了。。。
对于有下界的网络流,直接把每条边下界补齐可能流量不平衡。观察每个点的入流和出流,若进入的下界流比出去的下界流大a,则说明出去的非下界流要比进来的非下界流大a,因此建立超级源向这个点连一条流量为a的边,意味着只要这条边满流,就可以将这个点出边的流量合计扩充a,从而在流量守恒的条件下满足下界限制。出去的下界流大于进入的下界流同理,向超级汇连边。
如果原图有源汇的话还要从汇到源连一条无穷边使其转换为无源汇的图,每个点都满足流量守恒。
这样跑一遍从超级源到超级汇的最大流就是可行环流,也是从汇到源连的那条边的流量。若超级源连出的边不满流则无解。
最大流:删去超级源、超级汇所连的边以及从汇到源的边,在残量网络上再跑最大流,与第一次的可行流相加即为最大流。
最小流:先不连从汇到源的边,跑完一次后再连上那条边跑一次,这条边上流量就是最小流。实际上第一次的作用相当于尽可能把流量导到那条边以外的边上,类似于匹配问题把优先匹配的边先连上进行匹配,再连其他边(例如挑战NPC)
带费用的目前还没见过,那道叫支线剧情的被线性规划秒了。。。
然后这题倒着DP一下,将所有满足f[i]+g[j]==ans的(i,j)连下界为1的边,按上面方法跑一下最小流,光荣TLE。
于是这题显然一定有可行解,回顾最小流的过程,第二次流完后的流量就是连出去边的总流量,这个我们是知道的,于是只需要跑第一次,用满流减去第一次,就是最小流了。这就可以过了。 为啥ISAP暴力跑最小流就能过啊
代码:
#include<cstdio>#include<cstring>#include<algorithm>#include<ext/pb_ds/assoc_container.hpp>#define gm 50010int n;struct pnt{ int x,y; void get() { scanf("%d%d",&x,&y); } bool operator< (const pnt &b) const { return (y!=b.y)?(y<b.y):(x<b.x); }}p[gm],o[gm];int no[gm];typedef __gnu_pbds::gp_hash_table<int,int> map;int f[gm],g[gm],h[gm];int ans=0,tail=0;map ln,zs,ys;int from[gm],__from[gm];void print_path(int a){ if(!a) return; int b=from[a]; if(p[a].y!=p[b].y) print_path(b); else { print_path(__from[b]); if(p[a].x<p[b].x) { for(int i=b;p[i].y==p[b].y;++i) printf("%d ",no[i]); for(int i=b-1;p[i].x>p[a].x;--i) printf("%d ",no[i]); } else { for(int i=b;p[i].y==p[b].y;--i) printf("%d ",no[i]); for(int i=b+1;p[i].x<p[a].x;++i) printf("%d ",no[i]); } } printf("%d%c",no[a],a==tail?'\n':' ');}int s,t,ss,tt;const int inf=0x7f7f7f7f;struct e{ int t; e *n; int flow; e *r; e(int t,e *n,int flow):t(t),n(n),flow(flow){}}*r[gm];bool ingr[gm];int rd[gm];void link(int a,int b,int flow){ r[a]=new e(b,r[a],flow); r[b]=new e(a,r[b],0); r[a]->r=r[b]; r[b]->r=r[a];}void adde(int a,int b){ ingr[a]=ingr[b]=1; ++rd[b],--rd[a]; link(a,b,inf);}int d[gm];bool bfs(){ memset(d,-1,n+5<<2); static int q[gm]; int l=0,r=0; d[q[++r]=ss]=0; while(l!=r&&d[tt]==-1) { int x=q[++l]; for(e *i=::r[x];i;i=i->n) if(i->flow&&d[i->t]==-1) d[q[++r]=i->t]=d[x]+1; } return d[tt]!=-1;}int send(int x=ss,int flow=inf){ if(x==tt) return flow; int last=flow; for(e *i=r[x];i&&last;i=i->n) if(i->flow&&d[i->t]==d[x]+1) { int kre=send(i->t,i->flow<last?i->flow:last); last-=kre; i->flow-=kre; i->r->flow+=kre; } if(last) d[x]=-1; return flow-last;}int minflow(){ int ans=0; for(int i=0;i<=n;++i) if(ingr[i]) { link(s,i,inf); link(i,t,inf); if(rd[i]>0) { link(ss,i,rd[i]); ans+=rd[i]; } else if(rd[i]<0) { link(i,tt,-rd[i]); } } while(bfs()) ans-=send(); return ans;}int main(){ scanf("%d",&n); s=n+1,t=n+2,ss=n+3,tt=n+4; for(int i=1;i<=n;++i) { p[i].get();o[i]=p[i]; } std::sort(p+1,p+n+1); for(int i=1;i<=n;++i) no[std::lower_bound(p+1,p+n+1,o[i])-p]=i; ln[0]=zs[0]=ys[0]=0; for(int i=1,j=1;i<=n;i=j) { while(j<=n&&p[i].y==p[j].y) ++j; for(int k=i;k<j;++k) { f[k]=-1; map::point_iterator it; pnt &a=p[k]; it=ln.find(a.x); if(it!=ln.end()) { int q=it->second; if(f[q]!=-1&&f[q]+1>f[k]) { f[k]=f[q]+1; from[k]=q; if(f[k]>ans) ans=f[k],tail=k; } } it=zs.find(a.x+a.y); if(it!=zs.end()) { int q=it->second; if(f[q]!=-1&&f[q]+1>f[k]) { f[k]=f[q]+1; from[k]=q; if(f[k]>ans) ans=f[k],tail=k; } } it=ys.find(a.x-a.y); if(it!=ys.end()) { int q=it->second; if(f[q]!=-1&&f[q]+1>f[k]) { f[k]=f[q]+1; from[k]=q; if(f[k]>ans) ans=f[k],tail=k; } } h[k]=f[k]; __from[k]=from[k]; } for(int k=i;k<j;++k) { for(int l=i;l<k;++l) if(h[l]!=-1&&h[l]+(k-i)>f[k]) { f[k]=h[l]+(k-i); from[k]=l; if(f[k]>ans) ans=f[k],tail=k; } for(int l=k+1;l<j;++l) if(h[l]!=-1&&h[l]+(j-k-1)>f[k]) { f[k]=h[l]+(j-k-1); from[k]=l; if(f[k]>ans) ans=f[k],tail=k; } ln[p[k].x]=zs[p[k].x+p[k].y]=ys[p[k].x-p[k].y]=k; } } printf("%d\n",ans); print_path(tail); ln.clear();zs.clear();ys.clear(); for(int i=n,j=n;i>=0;i=j) { while(j>=0&&p[i].y==p[j].y) --j; for(int k=i;k>j;--k) { if(f[k]==-1) continue; g[k]=1; map::point_iterator it; pnt &a=p[k]; it=ln.find(a.x); if(it!=ln.end()) { int q=it->second; if(g[q]+1>g[k]) { g[k]=g[q]+1; } if(g[q]+f[k]==ans) { adde(k,q); } } it=zs.find(a.x+a.y); if(it!=zs.end()) { int q=it->second; if(g[q]+1>g[k]) { g[k]=g[q]+1; } if(g[q]+f[k]==ans) { adde(k,q); } } it=ys.find(a.x-a.y); if(it!=ys.end()) { int q=it->second; if(g[q]+1>g[k]) { g[k]=g[q]+1; } if(g[q]+f[k]==ans) { adde(k,q); } } h[k]=g[k]; } for(int k=i;k>j;--k) { if(f[k]==-1) continue; for(int l=i;l>k;--l) { if(h[l]+(l-j-1)>g[k]) { g[k]=h[l]+(l-j-1); } } for(int l=k-1;l>j;--l) { if(h[l]+(i-l)>g[k]) { g[k]=h[l]+(i-l); } } ln[p[k].x]=zs[p[k].x+p[k].y]=ys[p[k].x-p[k].y]=k; } } printf("%d\n",minflow()); return 0;}
- 【bzoj4200】【NOI2015】【小园丁与老司机】【dp+最小流】
- BZOJ4200: [Noi2015]小园丁与老司机 最小流
- BZOJ4200: [Noi2015]小园丁与老司机
- bzoj4200 [NOI2015]小园丁与老司机
- [DP 上下界最小流] BZOJ4200 [Noi2015]小园丁与老司机
- bzoj4200 [Noi2015]小园丁与老司机(dp+记录路径+有源汇有上下界最小流)
- 【bzoj4200】[Noi2015]小园丁与老司机 dp+有上下界的网络流
- 【NOI2015】小园丁与老司机
- 4200: [Noi2015]小园丁与老司机
- 【NOI2015】小园丁与老司机 DP 网络流
- 4200: [Noi2015]小园丁与老司机 DP+有源汇上下界最小流
- 小园丁与老司机
- BZOJ 4200 NOI 2015 小园丁与老司机
- 小白跟老司机学网站搭建
- 网易老司机:网络性能问题该怎样监控与定位?
- 前端老司机与算法的四个故事
- Django 老司机
- 老司机开车记
- Python-OpenCV人脸检测(代码)
- Design各控件的搭配使用2
- Design各控件的搭配使用3
- Design各控件的搭配使用4
- QiYuAdmin-bootstrap.addtabs美化(SpringBoot实战)
- BZOJ4200: [Noi2015]小园丁与老司机 最小流
- iconify使用文本替换icon测试
- 图的广度优先遍历
- AppBarLayout.OnOffsetChangedListener的使用
- 程序运行时内存分布
- 多线程(十八)AQS(四):CLH同步队列
- Android百分比布局Percent支持库
- Android本地Json配置文件实现笔记
- ParallaxViewPager:ViewPager的视差背景效果