【BZOJ4200】[Noi2015]小园丁与老司机 DP+最小流
【BZOJ2839】[Noi2015]小园丁与老司机
Description
Input
输入文件的第 1 行包含 1 个正整数 n,表示许愿树的数量。
Output
Sample Input
-1 1
1 1
-2 2
0 8
0 9
0 10
Sample Output
2 1 3
3
explanation
最优路线 2 条可许愿 3 次:(0,0)→(1,1)→(−1,1)→(−2,2) 或 (0,0)→(0,8)→(0,9)→(0,10)(0,0)→(0,8)→(0,9)→(0,10)。 至少 3 台轧路机,路线是 (0,0)→(1,1)(0,0)→(1,1),(−1,1)→(−2,2)和 (0,0)→(0,8)→(0,9)→(0,10)。
题解:超级码农题,我一共进行了下面5个操作:
1.预处理出每个点在5个方向上最近的点。
2.正着进行第一次DP,求出f[i]表示从原点到i的最长路径长度,顺便记录个路径。
3.递归输出路径。
4.反着再来一次DP,求出哪些路径在最优路线上。
5.求最小流。
细节有点多,自己的做法也有点麻烦,一个一个说吧~
1.排个序,开3个map搞定。
2.向上转移倒是容易,但是左右转移就有点麻烦了。用f0表示由向上转移得到的DP值,f1表示由左右转移或向上转移得到的DP值。我们先从左往右扫一遍,用f0的前缀最大值+i左边的点的个数来更新i的f1。顺便记录一下i的f0和f1都是从谁转移过来的。再同样的从右往左扫一遍。
3.递归输出即可。如果有左右转移,要把左(右)边所有点都输出,具体顺序好像无所谓。
4.最恶心的一步!因为要求最小流,所以我们要把所有在最优路线上的点都找出来,一个可行的方法就是反过来DP一遍,用g[i]表示从任意一个最优的结束点到i的最长路径长度。如果f[i]+g[j]=最长路径长度,那么i-j这条边在最优路线上。
但是我的方法更麻烦。给每个节点都打一个vis标记,起初最优结束点的vis=2。如果vis&1说明它可以向下传递标记,如果vis&2说明它可以左右传递标记。如果标记能沿着一条路径传递,则说明这条路径在最优路线上。具体地,vis&2的标记在左右传递后vis变成1,vis&1的标记在向下传递后vis变成2,如果一个点的f0=f1,且vis=1或2,那么vis=3。感觉正常人应该不懂我在说什么~
5.最小流。这里学到了一种求最小流的新方法:先不练T到S的那条边,跑从SS到TT的最大流,设流量为x1,再连从T到S,容量inf的边,跑从SS到TT的最大流,设增加的流量为x2,如果第二次满流则说明有解。但是本题一定是能满流的,所以不需要跑第二次,甚至不需要S和T两点,直接用满流-x1就行了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <map>
#include <algorithm>
using namespace std;
const int maxn=50010;
const int inf=1<<30;
//0左1右2上3左上4右上
typedef long long ll;
struct tree
{
int x,y,np[5],vis,org,ls,rs,p1,p2;
}p[maxn];
int n,cnt,S,T,flag,top;
ll tot,ans;
map<int,int> m2,m3,m4;
int f0[maxn],f1[maxn],mn[maxn],st[maxn],d[maxn],p1[maxn],p2[maxn];
int to[1000000],next[1000000],val[1000000],head[maxn];
queue<int> q;
bool cmpy(tree a,tree b)
{
return (a.y==b.y)?(a.x<b.x):(a.y<b.y);
}
void getnp()
{
sort(p,p+n+1,cmpy);
int i,j;
for(i=n;i;i--)
{
p[i].np[2]=m2[p[i].x],p[i].np[3]=m3[p[i].x+p[i].y],p[i].np[4]=m4[p[i].x-p[i].y];
if(p[i-1].y==p[i].y) p[i-1].np[1]=i,p[i].np[0]=i-1;
m2[p[i].x]=m3[p[i].x+p[i].y]=m4[p[i].x-p[i].y]=i;
}
for(i=1;i<=n;i++)
{
if(!p[i].np[1]) for(j=p[i].np[0];j;j=p[j].np[0]) p[j].rs=p[p[j].np[1]].rs+1;
if(!p[i].np[0]) for(j=p[i].np[1];j;j=p[j].np[1]) p[j].ls=p[p[j].np[0]].ls+1;
}
}
void print(int a)
{
if(a==1) return ;
if(p[p1[a]].y==p[a].y)
{
print(p2[p1[a]]);
int j;
if(p[p1[a]].x<p[a].x)
{
for(j=p1[a];j;j=p[j].np[0]) printf("%d ",p[j].org);
for(j=p[p1[a]].np[1];j!=a;j=p[j].np[1]) printf("%d ",p[j].org);
}
else
{
for(j=p1[a];j;j=p[j].np[1]) printf("%d ",p[j].org);
for(j=p[p1[a]].np[0];j!=a;j=p[j].np[0]) printf("%d ",p[j].org);
}
}
else print(p1[a]);
printf("%d ",p[a].org);
}
void add(int a,int b,int c)
{
to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
to[cnt]=a,val[cnt]=0,next[cnt]=head[b],head[b]=cnt++;
//printf("*%d %d %d\n",a,b,c);
}
void DP()
{
//0从下面上来的1同行转移
memset(f0,0x80,sizeof(f0));
memset(f1,0x80,sizeof(f1));
int i,j,k,mx,mf;
f0[1]=f1[1]=0;
for(i=1;i<=n;i++)
{
if(!p[i].np[0]) for(mx=0,j=i;j;j=p[j].np[1])
{
f1[j]=f0[j],p1[j]=p2[j];
if(f0[mx]+p[j].ls>f1[j]) f1[j]=f0[mx]+p[j].ls,p1[j]=mx;
if(f0[j]>f0[mx]) mx=j;
}
if(!p[i].np[1])
{
for(mx=0,j=i;j;j=p[j].np[0])
{
if(f0[mx]+p[j].rs>f1[j]) f1[j]=f0[mx]+p[j].rs,p1[j]=mx;
if(f0[j]>f0[mx]) mx=j;
}
for(j=i;j;j=p[j].np[0]) for(k=2;k<=4;k++) if(p[j].np[k])
if(f0[p[j].np[k]]<f1[j]+1) f0[p[j].np[k]]=f1[j]+1,p2[p[j].np[k]]=j;
}
}
for(mf=n+1,i=1;i<=n;i++) if(f1[i]>f1[mf]) mf=i;
printf("%d\n",f1[mf]);
print(mf);
printf("\n");
for(i=n;i>=1;i--)
{
if(!p[i].np[1])
{
for(top=0,mx=-inf,j=i;j;j=p[j].np[0])
{
if(f1[j]==f1[mf]) p[j].vis=2;
for(k=2;k<=4;k++) if(p[j].np[k])
{
if((p[p[j].np[k]].vis&1)&&f0[p[j].np[k]]==f1[j]+1)
{
mn[p[j].np[k]]++,mn[j]--,add(j,p[j].np[k],inf),p[j].vis|=2;
}
}
if(p[j].vis&&f1[j]==f0[j]) p[j].vis|=1;
if((p[j].vis&2)&&mx+p[j].rs==f1[j])
{
while(top) p[st[top--]].vis|=1;
}
if(f0[j]>mx) mx=f0[j],top=0;
if(!(p[j].vis&1)&&f0[j]==mx) st[++top]=j;
}
}
if(!p[i].np[0])
{
for(top=0,mx=-inf,j=i;j;j=p[j].np[1])
{
if((p[j].vis&2)&&mx+p[j].ls==f1[j])
{
while(top) p[st[top--]].vis|=1;
}
if(f0[j]>mx) mx=f0[j],top=0;
if(!(p[j].vis&1)&&f0[j]==mx) st[++top]=j;
}
}
}
}
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;
}
int dfs(int x,int mf)
{
if(x==T) return mf;
int k,temp=mf,i;
for(i=head[x];i!=-1;i=next[i])
{
if(d[to[i]]==d[x]+1&&val[i])
{
k=dfs(to[i],min(temp,val[i]));
if(!k) d[to[i]]=0;
val[i]-=k,val[i^1]+=k,temp-=k;
if(!temp) break;
}
}
return mf-temp;
}
int bfs()
{
memset(d,0,sizeof(d));
while(!q.empty()) q.pop();
int i,u;
q.push(S),d[S]=1;
while(!q.empty())
{
u=q.front(),q.pop();
for(i=head[u];i!=-1;i=next[i])
{
if(!d[to[i]]&&val[i])
{
d[to[i]]=d[u]+1;
if(to[i]==T) return 1;
q.push(to[i]);
}
}
}
return 0;
}
int main()
{
n=rd();
int i;
memset(head,-1,sizeof(head));
for(i=1;i<=n;i++) p[i].x=rd(),p[i].y=rd(),p[i].org=i;
n++;
getnp(),DP();
S=n+1,T=n+2;
for(i=1;i<=n;i++)
{
if(mn[i]>0) tot+=mn[i],add(S,i,mn[i]);
if(mn[i]<0) add(i,T,-mn[i]);
}
while(bfs()) ans+=dfs(S,inf);
printf("%lld",tot-ans);
return 0;
}
【BZOJ4200】[Noi2015]小园丁与老司机 DP+最小流的更多相关文章
- bzoj4200: [Noi2015]小园丁与老司机(可行流+dp)
传送门 这该死的码农题…… 题解在这儿->这里 //minamoto #include<iostream> #include<cstdio> #include<cs ...
- [BZOJ4200][Noi2015]小园丁与老司机
4200: [Noi2015]小园丁与老司机 Time Limit: 20 Sec Memory Limit: 512 MBSec Special JudgeSubmit: 106 Solved ...
- luogu P2304 [NOI2015]小园丁与老司机 dp 上下界网络流
LINK:小园丁与老司机 苦心人 天不负 卧薪尝胆 三千越甲可吞吴 AC的刹那 真的是泪目啊 很久以前就写了 当时记得特别清楚 写到肚子疼.. 调到胳膊疼.. ex到根不不想看的程度. 当时wa了 一 ...
- BZOJ4200 NOI2015小园丁与老司机(动态规划+上下界网络流)
一看上去就是一个二合一的题.那么先解决第一部分求最优路线(及所有可能在最优路线上的线段). 由于不能往下走,可以以y坐标作为阶段.对于y坐标不同的点,我们将可以直接到达的两点连边,显然这样的边的个数是 ...
- [NOI2015]小园丁与老司机(DP+上下界最小流)
由于每行点的个数不超过1000,所以行内DP可以使用$O(n^2)$算法. 先找到每个点所能直接到达的所有点(x,y,x+y或x-y相同),用排序实现. 第一问:以行为阶段,对于每行,暴力枚举最有路径 ...
- UOJ#132&bzoj4200[Noi2015]小园丁与老司机
看,这是一个传送门 Part A 把坐标离散化,按照纵坐标为第一关键字,横坐标为第二关键字排序 以$f_i$记录来到$i$这个点最多经过点数,那么答案显而易见就是$f_i$加上该层点数 转移的话就是分 ...
- [UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机
[UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机 试题描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 \(n\) 棵许愿 ...
- uoj132/BZOJ4200/洛谷P2304 [Noi2015]小园丁与老司机 【dp + 带上下界网络流】
题目链接 uoj132 题解 真是一道大码题,,,肝了一个上午 老司机的部分是一个\(dp\),观察点是按\(y\)分层的,而且按每层点的上限来看可以使用\(O(nd)\)的\(dp\),其中\(d\ ...
- 【bzoj4200】[Noi2015]小园丁与老司机 STL-map+dp+有上下界最小流
题目描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2,3,…,n,每棵树可以看作平面上的一个点,其中第 ii 棵树 (1≤ ...
随机推荐
- cocoapods集成三方库遇到的坑
什么都不想说直接上图 这是最近在管理三方库时遇到头疼的问题,刚开始一直怀疑是cocoapods或者ruby的版本问题但是升级到最新版还是同样的错误,后来又怀疑是资源文件的问题但是在同一时间不同地点集成 ...
- HashMap之equals和hashCode小陷阱
先以一段代码开始这篇blog. 01 public class Name { 02 03 private String first; //first name 04 private Str ...
- log4j 2使用properties文件进行配置
网上不少文章给的都是用xml进行配置,也会提到无法使用properties文件对log4j进行配置,但那应该只是在他们写文章的时候才是如此,最新的2.8.2版本经过我试验后是可以做到的当然该文件最好放 ...
- 基于WPF系统框架设计(5)-Ribbon整合Avalondock 2.0实现多文档界面设计(二)
AvalonDock 是一个.NET库,用于在停靠模式布局(docking)中排列一系列WPF/WinForm控件.最新发布的版本原生支持MVVM框架.Aero Snap特效并具有更好的性能. Ava ...
- nginx 按天生成日志
nginx日志access.log error.log按天生成存储,定时删除日志 logrotate https://linux.cn/article-4126-1.html http://blog. ...
- Linux下ntp时间同步
在root用户下执行 先安装同步时间软件,每台机器执行 yum install -y ntp 然后执行以下命令: crontab -e */10 * * * * /usr/sbin/ntpdate - ...
- C 语言实例
C 语言实例 C 语言实例 - 输出 "Hello, World!" C 语言实例 - 输出整数 C 语言实例 - 两个数字相加 C 语言实例 - 两个浮点数相乘 C 语言实例 - ...
- html小知识,怎么实现一个td占据2行
<table border="1" width="100%"> <tr> <td rowspan="2"> ...
- OSX:设置用户默认浏览器
近期我们遇到的情况是,须要统一设置用户的默认浏览器为Google Chrome.而系统默认的是Safari. 这个设置是系统Launch Services基于用户管理的. 意思就是说,即便是改动了系统 ...
- jquery相冊图片来回选择
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <script sr ...