【BZOJ4200】【NOI2015】小园丁与老司机(动态规划,网络流)

题面

BZOJ权限题,洛谷链接

题解

一道二合一的题目

考虑第一问。

先考虑如何计算六个方向上的第一个点。

左右上很好考虑,只需要按照\(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】小园丁与老司机(动态规划,网络流)的更多相关文章

  1. [BZOJ4200][Noi2015]小园丁与老司机

    4200: [Noi2015]小园丁与老司机 Time Limit: 20 Sec  Memory Limit: 512 MBSec  Special JudgeSubmit: 106  Solved ...

  2. BZOJ4200 NOI2015小园丁与老司机(动态规划+上下界网络流)

    一看上去就是一个二合一的题.那么先解决第一部分求最优路线(及所有可能在最优路线上的线段). 由于不能往下走,可以以y坐标作为阶段.对于y坐标不同的点,我们将可以直接到达的两点连边,显然这样的边的个数是 ...

  3. UOJ#132&bzoj4200[Noi2015]小园丁与老司机

    看,这是一个传送门 Part A 把坐标离散化,按照纵坐标为第一关键字,横坐标为第二关键字排序 以$f_i$记录来到$i$这个点最多经过点数,那么答案显而易见就是$f_i$加上该层点数 转移的话就是分 ...

  4. bzoj4200: [Noi2015]小园丁与老司机(可行流+dp)

    传送门 这该死的码农题…… 题解在这儿->这里 //minamoto #include<iostream> #include<cstdio> #include<cs ...

  5. [UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机

    [UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机 试题描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 \(n\) 棵许愿 ...

  6. 【BZOJ4200】[Noi2015]小园丁与老司机 DP+最小流

    [BZOJ2839][Noi2015]小园丁与老司机 Description 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2, ...

  7. luogu P2304 [NOI2015]小园丁与老司机 dp 上下界网络流

    LINK:小园丁与老司机 苦心人 天不负 卧薪尝胆 三千越甲可吞吴 AC的刹那 真的是泪目啊 很久以前就写了 当时记得特别清楚 写到肚子疼.. 调到胳膊疼.. ex到根不不想看的程度. 当时wa了 一 ...

  8. uoj132/BZOJ4200/洛谷P2304 [Noi2015]小园丁与老司机 【dp + 带上下界网络流】

    题目链接 uoj132 题解 真是一道大码题,,,肝了一个上午 老司机的部分是一个\(dp\),观察点是按\(y\)分层的,而且按每层点的上限来看可以使用\(O(nd)\)的\(dp\),其中\(d\ ...

  9. BZOJ4200 & 洛谷2304 & UOJ132:[NOI2015]小园丁与老司机——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4200 https://www.luogu.org/problemnew/show/P2304 ht ...

  10. 【bzoj4200】[Noi2015]小园丁与老司机 STL-map+dp+有上下界最小流

    题目描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2,3,…,n,每棵树可以看作平面上的一个点,其中第 ii 棵树 (1≤ ...

随机推荐

  1. 查看Oracle数据库表空间大小(空闲、已使用),是否要增加表空间的数据文件

    查看Oracle数据库表空间大小(空闲.已使用),是否要增加表空间的数据文件 1.查看表空间已经使用的百分比 Sql代码 select a.tablespace_name,a.bytes/1024/1 ...

  2. iOS 小技巧

    投影效果 scanBtn.layer.shadowColor = [UIColorblackColor].CGColor;//shadowColor阴影颜色     scanBtn.layer.sha ...

  3. HTML基本代码教学片,认识HTML

    今儿头午有点晕晕的感觉,咳咳,甩甩头开课 HTML 定义:超文本标记语言 (记不住的可以这么记:how to make love ! 哈哈,准备开车,粗人一个,长的不行) 其实理解起来很简单,超越文本 ...

  4. Android开发笔记——视频录制播放常见问题

    本文分享自己在视频录制播放过程中遇到的一些问题,主要包括: 视频录制流程 视频预览及SurfaceHolder 视频清晰度及文件大小 视频文件旋转 一.视频录制流程 以微信为例,其录制触发为按下(住) ...

  5. exe4j 使用记录(一):下载、安装及注册

    一.下载 exe4j下载地址:https://www.ej-technologies.com/download/exe4j/files 进入下载页面,可以选择exe4j版本. 选择完成后,下载即可. ...

  6. JavaScript查找元素的方法

    1.根据id获取元素 document.getElementById("id属性的值"); 2.根据标签名字获取元素 document.getElementsByTagName(& ...

  7. 阿里巴巴将在美国推出电子商务网站11 Main

    新浪科技讯 北京时间2月11日晚间消息,阿里巴巴集团周二向路透社证实,阿里巴巴将通过旗下子公司Vendio和Auctiva在美国推出一个电子商务网站. 该网站的名称为“11 Main”(11main. ...

  8. [leetcode-915-Partition Array into Disjoint Intervals]

    Given an array A, partition it into two (contiguous) subarrays left and right so that: Every element ...

  9. 互评Alpha版本——二次元梦之队——“I Do”

    基于NABCD评论作品,及改进建议 1.根据(不限于)NABCD评论作品的选题 (1)N(Need,需求) 随着智能科技的发展和普及,编程教育的重要性已经逐渐凸显出来.美国前总统奥巴马曾说“编程应当与 ...

  10. 作业三C++

    作业心得 1.本次作业开始使用C++编写了(面向过程的C++,2333) 2.粗略学习了一下文件输入输出,和项目的创建等(在大佬眼里最基本的操作QAQ,然而我还是有点晕晕的,平时都是ctrl+n新建源 ...