【BZOJ4200】【NOI2015】小园丁与老司机(动态规划,网络流)
【BZOJ4200】【NOI2015】小园丁与老司机(动态规划,网络流)
题面
题解
一道二合一的题目
考虑第一问。
先考虑如何计算六个方向上的第一个点。
左右上很好考虑,只需要按照\(x\)或者\(y\)轴排序就行了。
对于\(45\)度的斜角,两点一定在同一条直线上。
这条直线是\(x+y=b\)或\(x-y=b\)
所以按照\(x+y\)和\(x-y\)的值分类考虑,再按照顺序在\(x\)轴扫一遍就可以找到了。
考虑如何计算第一问的答案,我们发现\(y\)轴是单调不降的。
所以可以以\(y\)来划分截断,按照\(y\)轴从上往下进行转移。
设\(f[i]\)表示\(i\)的答案,对于所有\(y\)值相同的点放在一起解决。
每次转移的时候,先将当前\(y\)相同的所有点通过左上右上和上三个方向进行转移。
然后再考虑左右的转移。因为左右转移是可以先向一个方向绕过去再走回来的,
所以考虑转移的时候需要额外注意一下。
所以再诶外设一个\(g\),\(f[i]\)表示从\(i\)出发不在同层内转移的最优答案。
\(g[i]\)表示从\(i\)出发,先转移到同层的某个点的最优答案。
第二问
先把所有可以存在于一个最优方案下的点连边。
因为每条边都至少被覆盖一次,发现这是一个上下界网络流问题。
因为保证有解,直接解决就好了
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<vector>
using namespace std;
#define ll long long
#define RG register
#define MAX 50505
#define inf 1e6
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
int n;
struct Node{int x,y,id;}p[MAX];
bool cmpx(Node a,Node b){if(a.x!=b.x)return a.x<b.x;return a.y<b.y;}
bool cmpy(Node a,Node b){if(a.y!=b.y)return a.y<b.y;return a.x<b.x;}
bool cmpn(Node a,Node b){return a.id<b.id;}
int nt[MAX][5];
map<int,int> M;
int f[MAX],g1[MAX],g2[MAX],g[MAX],zy[MAX],a[MAX];
namespace Task1
{
void Getdirt()
{
sort(&p[1],&p[n+1],cmpx);
for(int i=1;i<n;++i)if(p[i].x==p[i+1].x)nt[p[i].id][2]=p[i+1].id;
for(int i=1;i<=n;++i)if(p[i].x==0){nt[0][2]=p[i].id;break;}//up
M.clear();
for(int i=1;i<=n;++i)//left-up
{
int s=p[i].x+p[i].y;
if(M[s])nt[p[i].id][3]=M[s];
M[s]=p[i].id;
if(s==0)nt[0][3]=p[i].id;
}
M.clear();
for(int i=n;i>=1;--i)//right-up
{
int s=p[i].y-p[i].x;
if(M[s])nt[p[i].id][4]=M[s];
M[s]=p[i].id;
if(s==0)nt[0][4]=p[i].id;
}
}
int st[MAX],top;
void Output(int x,int ans)
{
if(!ans)return;
if(f[x]==ans){printf("%d ",x);Output(zy[x],ans-1);return;}
int pos=a[x];while(pos>1&&p[pos-1].y==p[a[x]].y)--pos;
top=0;while(p[pos].y==p[a[x]].y&&pos<=n)st[++top]=p[pos++].id;
for(int i=1;i<=top;++i)if(st[i]==x){pos=i;break;}
for(int i=1;i<pos;++i)
if(f[st[i]]+top-i==g[x])
{
for(int j=pos;j<=top;++j)printf("%d ",st[j]);
for(int j=pos-1;j>i;--j)printf("%d ",st[j]);
Output(st[i],f[st[i]]);
return;
}
for(int i=top;i>pos;--i)
if(f[st[i]]+i-1==g[x])
{
for(int j=pos;j>=1;--j)printf("%d ",st[j]);
for(int j=pos+1;j<i;++j)printf("%d ",st[j]);
Output(st[i],f[st[i]]);
return;
}
}
void Solve()
{
Getdirt();sort(&p[1],&p[n+1],cmpy);
for(int i=1;i<=n;++i)a[p[i].id]=i;
for(int i=n,pos;i;i=pos)
{
top=0;pos=i;
while(pos&&p[pos].y==p[i].y)st[++top]=p[pos--].id;
reverse(&st[1],&st[top+1]);
for(int j=1;j<=top;++j)
for(int k=2;k<=4;++k)
{
int u=st[j];
if(f[u]<g[nt[u][k]]+1)
f[u]=g[nt[u][k]]+1,zy[u]=nt[u][k];
}
for(int j=1;j<=top;++j)g[st[j]]=f[st[j]];
for(int j=2,nw=1;j<=top;++j)
{
g[st[j]]=max(g[st[j]],f[st[nw]]+top-nw);
if(f[st[nw]]-nw<f[st[j]]-j)nw=j;
}
for(int j=top-1,nw=top;j>=1;--j)
{
g[st[j]]=max(g[st[j]],f[st[nw]]+nw-1);
if(f[st[nw]]+nw<f[st[j]]+j)nw=j;
}
}
for(int k=2;k<=4;++k)
if(f[0]<=g[nt[0][k]])f[0]=g[nt[0][k]],zy[0]=nt[0][k];
printf("%d\n",f[0]);
Output(zy[0],f[0]);puts("");
}
}
namespace Task2
{
struct Line{int v,next,w;}e[MAX<<5];
int h[MAX],cnt=2,M[MAX],SS,TT,S,T;
inline void Add(int u,int v,int w)
{
e[cnt]=(Line){v,h[u],w};h[u]=cnt++;
e[cnt]=(Line){u,h[v],0};h[v]=cnt++;
}
set<pair<int,int> > SSS;
bool vis[MAX];
void Link(int x,int ans,bool fl)
{
if(!ans)return;
if(!fl)if(SSS.count(make_pair(x,ans)))return;
if(!fl)SSS.insert(make_pair(x,ans));
if(f[x]==ans&&!vis[x])
{
vis[x]=true;
for(int k=2;k<=4;++k)
if(f[x]==g[nt[x][k]]+1)
{
Add(x,nt[x][k],inf);
M[x]-=1;M[nt[x][k]]+=1;
Link(nt[x][k],ans-1,1);
}
}
if(!fl)return;
vector<int> st;int top=0;st.clear();st.push_back(0);
int pos=a[x];while(pos>1&&p[pos-1].y==p[a[x]].y)--pos;
top=0;while(p[pos].y==p[a[x]].y&&pos<=n)st.push_back(p[pos++].id),++top;
for(int i=1;i<=top;++i)if(st[i]==x){pos=i;break;}
for(int i=1;i<pos;++i)if(f[st[i]]+top-i==g[x])Link(st[i],f[st[i]],0);
for(int i=top;i>pos;--i)if(f[st[i]]+i-1==g[x])Link(st[i],f[st[i]],0);
}
int level[MAX],cur[MAX];
bool bfs(int S,int T)
{
memset(level,0,sizeof(level));level[S]=1;
queue<int> Q;Q.push(S);
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int i=h[u];i;i=e[i].next)
if(e[i].w&&!level[e[i].v])
level[e[i].v]=level[u]+1,Q.push(e[i].v);
}
return level[T];
}
int dfs(int u,int T,int flow)
{
if(u==T||!flow)return flow;
int ret=0;
for(int &i=cur[u];i;i=e[i].next)
{
int v=e[i].v,d;
if(e[i].w&&level[v]==level[u]+1)
{
d=dfs(v,T,min(flow,e[i].w));
ret+=d;flow-=d;
e[i].w-=d;e[i^1].w+=d;
if(!flow)break;
}
}
return ret;
}
int Dinic(int S,int T)
{
int ret=0;
while(bfs(S,T))
{
memcpy(cur,h,sizeof(cur));
ret+=dfs(S,T,inf);
}
return ret;
}
int ans,rb;
void Solve()
{
S=n+1;T=n+2;SS=n+3;TT=n+4;
f[0]+=1;g[0]=inf;Link(0,f[0],0);
for(int i=0;i<=n;++i)Add(i,T,inf);
for(int i=0;i<=n;++i)Add(S,i,inf);
for(int i=0;i<=n;++i)
if(M[i]>0)Add(SS,i,M[i]);
else Add(i,TT,-M[i]);
Add(T,S,inf);rb=cnt-1;
Dinic(SS,TT);
h[T]=e[h[T]].next;h[S]=e[h[S]].next;
ans-=Dinic(T,S);ans+=e[rb].w;
printf("%d\n",ans);
}
}
int main()
{
n=read();
for(int i=1;i<=n;++i)p[i].x=read(),p[i].y=read(),p[i].id=i;
Task1::Solve();Task2::Solve();
return 0;
}
【BZOJ4200】【NOI2015】小园丁与老司机(动态规划,网络流)的更多相关文章
- [BZOJ4200][Noi2015]小园丁与老司机
4200: [Noi2015]小园丁与老司机 Time Limit: 20 Sec Memory Limit: 512 MBSec Special JudgeSubmit: 106 Solved ...
- BZOJ4200 NOI2015小园丁与老司机(动态规划+上下界网络流)
一看上去就是一个二合一的题.那么先解决第一部分求最优路线(及所有可能在最优路线上的线段). 由于不能往下走,可以以y坐标作为阶段.对于y坐标不同的点,我们将可以直接到达的两点连边,显然这样的边的个数是 ...
- UOJ#132&bzoj4200[Noi2015]小园丁与老司机
看,这是一个传送门 Part A 把坐标离散化,按照纵坐标为第一关键字,横坐标为第二关键字排序 以$f_i$记录来到$i$这个点最多经过点数,那么答案显而易见就是$f_i$加上该层点数 转移的话就是分 ...
- bzoj4200: [Noi2015]小园丁与老司机(可行流+dp)
传送门 这该死的码农题…… 题解在这儿->这里 //minamoto #include<iostream> #include<cstdio> #include<cs ...
- [UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机
[UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机 试题描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 \(n\) 棵许愿 ...
- 【BZOJ4200】[Noi2015]小园丁与老司机 DP+最小流
[BZOJ2839][Noi2015]小园丁与老司机 Description 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2, ...
- luogu P2304 [NOI2015]小园丁与老司机 dp 上下界网络流
LINK:小园丁与老司机 苦心人 天不负 卧薪尝胆 三千越甲可吞吴 AC的刹那 真的是泪目啊 很久以前就写了 当时记得特别清楚 写到肚子疼.. 调到胳膊疼.. ex到根不不想看的程度. 当时wa了 一 ...
- uoj132/BZOJ4200/洛谷P2304 [Noi2015]小园丁与老司机 【dp + 带上下界网络流】
题目链接 uoj132 题解 真是一道大码题,,,肝了一个上午 老司机的部分是一个\(dp\),观察点是按\(y\)分层的,而且按每层点的上限来看可以使用\(O(nd)\)的\(dp\),其中\(d\ ...
- BZOJ4200 & 洛谷2304 & UOJ132:[NOI2015]小园丁与老司机——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4200 https://www.luogu.org/problemnew/show/P2304 ht ...
- 【bzoj4200】[Noi2015]小园丁与老司机 STL-map+dp+有上下界最小流
题目描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2,3,…,n,每棵树可以看作平面上的一个点,其中第 ii 棵树 (1≤ ...
随机推荐
- Professional Books
Machine Learning: Pattern Recognition and Machine Learning(PRML) https://mqshen.gitbooks.io/p ...
- KETTLE:mongdb与mysql互传数据
注:部分内容引用了 http://blog.sina.com.cn/s/blog_4ac9f56e0101g881.html 1.mongodb传数据到mysql 1)在kettle中,mongodb ...
- 冲刺ing-4
第四次Scrum冲刺 队员完成的任务 队员 完成任务 吴伟华 Leangoo的看板截图,燃尽图 蔺皓雯 编写博客,学习后端设计 蔡晨旸 学习后端设计 曾茜 后端设计 鲁婧楠 服务器建构 杨池宇 学习后 ...
- vue cli3 配置postcss
1.安装postcss-import,postcss-cssnext 包 2.修改package.json 将postcss响应的内容替换为 "postcss": { " ...
- 细节--服务器mysql空密码
在部署致服务器的时候 发现mysql密码为空的情况 如果采用 root账户的话 试过很多 比如不写下面这行 <property name="password" value=& ...
- HDU 5855 Less Time, More profit 最大权闭合子图
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5855 Less Time, More profit Time Limit: 2000/1000 MS ...
- C语言的知识与能力的自评
1.我希望将来上班的地方是自己所感兴趣的,正在寻找自己感兴趣的,并且正在普及IT行业的相关知识. 2.我认为学习就是一个自我成长和自我提升以及认识世界的方法,学习的作用是可以不断的提升对这个世界的认识 ...
- Java中的网络编程-2
Socket编程:(一般的网络编程) <1> 两个 JAVA 应用程序可通过一个双向的网络通信连接, 实现数据交换, 这个双向链路的一段称为一个 Socket. <2> Soc ...
- gmssl
一.安装 1.1 github地址 1.2 官网地址 由于我本地虚拟机跑的是centos,按照官网的安装步骤,没有安装成功.后来使用github上提供的安装步骤完美编译安装成功. 二.使用 由于gms ...
- Beta阶段冲刺第一天
提供当天站立式会议照片一张 讨论项目每个成员的昨天进展 昨天开始了Beta阶段的冲刺,总体讨论了一下这个阶段的任务,然后明确了个人分工. 讨论项目每个成员的存在问题 第一天暂时还没有什么问题,可能最大 ...