【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. XDS100V3连接Pandaboard ES OMAP4460开发板

    1. 硬件连接如下 2. 使用CCS创建工程,不过好像没有ARM9的内核吧?为啥会出现? 3. 创建目标配置文件 4. 不过确实有ARM9的内核,两个A9内核,一个DSP C64X内核,两个M3的内核 ...

  2. postgresql parallel join example

    CREATE TABLE public.pgbench_accounts_bak ( aid integer NOT NULL, bid integer, abalance integer, fill ...

  3. stl源码分析之list

    本文主要分析gcc4.8版本的stl list的源码实现,与vector的线性空间结构不同,list的节点是任意分散的,节点之间通过指针连接,好处是在任何位置插入删除元素都只需要常数时间,缺点是不能随 ...

  4. 文件批量加密重命名--python脚本AND mysql命令行导入数据库

    在考试中学生交上来的报告,需要进行一下文件名加密,这样阅卷老师就不知道是谁的报告了 在百度帮助下,完成了加密和解密脚本, 加密 #!/usr/bin/python # -*- coding: utf- ...

  5. Your funds transfer has been delayed

    Hello from Amazon. Your funds transfer in the amount of 9,422.88 USD has been delayed because the cr ...

  6. redis利用key计时与计数

    计时 Setex 命令为指定的 key 设置值及其过期时间.如果 key 已经存在, SETEX 命令将会替换旧的值 基本命令: redis 127.0.0.1:6379> SETEX KEY_ ...

  7. C++ Primer Plus学习:第五章

    C++入门第五章:循环和关系表达式 for循环 for循环的组成部分 设置初始值. 执行测试,看循环是否应该继续执行. 执行循环操作. 更新用于测试的值. 以上操作由括号括起,每个部分均是一个表达式, ...

  8. Struts2(三)

    以下内容是基于导入struts2-2.3.32.jar包来讲的 1.全局视图配置 xml标签:<global-results> <result name="error&qu ...

  9. Scrum Meeting Beta - 1

    Scrum Meeting Beta - 1 NewTeam 2017/11/28 地点:主南201 任务反馈 团队成员 完成任务 计划任务 安万贺 详细讨论Beta阶段的任务和具体分工 了解缓存的相 ...

  10. 还原 listagg/wm_concat 后的数据 pack_split_listatt ;

    1.创建表并制作测试数据: --创建测试表 : CREATE TABLE split_table ( NAME ), ID ) ); --准备测试数据 : INSERT INTO split_tabl ...