NOI2015 小園丁與老司機

2021-08-26 09:09:02 字數 3345 閱讀 4107

給出n棵樹,從原點開始,每次可以在左、右、上、左上 45∘、右上 45∘五個方向中選擇乙個,然後一直向這個方向走,直至走到一棵未經過的樹,然後繼續選擇方向,直至5個方向都不存在未經過的樹。

現在要經過盡可能多的樹,輸出最多可能經過的樹的數量以及任意一條路徑。

現在定義向上、左上 45∘、右上 45∘三個方向走形成的線段不優美,現在要覆蓋所有最優路徑中的不優美線段,每次從原點出發,行走方式與之前相同,問至少幾次可以覆蓋這些線段。

這個問題可以拆成兩個子問題。

1.求最多經過的樹的數量並輸出任意一條路徑:

首先預處理出每棵樹和原點向上、左上 45∘、右上 45∘三個方向所能到達的樹,然後將y座標離散化,每一層根據x座標排序,然後就可以dp求解:

記dp[i]表示從i點進入這層所能到的樹的數量的最大值,自上而下逐層狀態轉移,轉移時可以發現對於從u進入該層,v出該層的轉態,可以經過該層中的樹的數量為:

1.u==v 只有1棵

2.u < v時 v和v左邊的所有樹。

3.u > v時 v和v右邊的所有樹。

然後只要對每一層掃兩遍即可dp,dp時順便記錄一下路徑即可。

2.求至少幾次覆蓋所有最優路徑中的不優美線段:

首先因為資料的約束,不優美的線段數量不可能太多,因此根據每個點的dp值,dfs出所有最優路徑(注意去重),然後問題就可以轉化為有上下界的最小流(每條邊的上限為inf,下限為1),因為圖較特殊,所以有較簡單的做法:

首先統計出每個點的入度(下界的入度和)與出度(下界的出度和)之差,若為正,則由超級源點向它連流量為差值的邊,並將其計入答案,若為負則由它向超級匯點連流量為該差值的絕對值的邊,此時的答案減去此圖的最大流即為需要的覆蓋次數。

#include

#include

#include

#include

#include

#include

#include

#define inf 0x3f3f3f3f

#define p pair

#define mp make_pair

#define fi first

#define se second

#define n 50010

using

namespace

std;

int n,cy,ty[n],dp[n],lj[n],mx[n],ml[n],pos[n],first[n],ds[n],deep[n],cur[n],bb=1,s,t,ans;

bool vis[n];

struct node

node[n],tp[n];

struct bn

bn[1001000];

vector

iy[n],to[n],can[n],st;

mapzhy;

mapbool>mm;

queue

que;

inline

bool cmp(const

int &u,const

int &v)

inline

void ad(int u,int v,int w)

inline

bool bfs()

}return deep[t];

}int dfs(int now,int mn)

}return0;}

inline

void out(int w,int u,int v)

if(u==v)

if(ufor(i=u;i>=0;i--) printf("%d ",iy[w][i]);

for(i=u+1;i<=v;i++) printf("%d ",iy[w][i]);

}else

}inline

int len(int w,int u,int v)

void dfs(int now)

sort(ty+1,ty+n+1);

for(i=1;i<=n;i++) if(ty[i]!=ty[i-1] || i==1) zhy[ty[i]]=++cy;

for(i=1;i<=n;i++) iy[zhy[node[i].y]].push_back(i);

for(i=1;i<=cy;i++) sort(iy[i].begin(),iy[i].end(),cmp);

sort(tp+1,tp+n+1,cmp1);for(i=1;i<=n;i++) if(tp[i].x==tp[i-1].x && i!=1) to[tp[i-1].id].push_back(tp[i].id);else

if(!tp[i].x) st.push_back(tp[i].id);

sort(tp+1,tp+n+1,cmp2);for(i=1;i<=n;i++) if(tp[i].x-tp[i].y==tp[i-1].x-tp[i-1].y && i!=1) to[tp[i-1].id].push_back(tp[i].id);else

if(tp[i].x==tp[i].y) st.push_back(tp[i].id);

sort(tp+1,tp+n+1,c***);for(i=1;i<=n;i++) if(tp[i].x+tp[i].y==tp[i-1].x+tp[i-1].y && i!=1) to[tp[i-1].id].push_back(tp[i].id);else

if(tp[i].x==-tp[i].y) st.push_back(tp[i].id);

if(!st.size())

for(i=cy;i>=1;i--)

else

if(dp[to[t][k]]==mx[t])

}if(mx[t]+1>dp[t])

}tmp=0;

for(j=iy[i].size()-2;j>=0;j--)

t=iy[i][j];

if(tmp>dp[t])

}tmp=0;

for(j=1;j1];

if(mx[t]+iy[i].size()-j+1>tmp)

t=iy[i][j];

if(tmp>dp[t])}}

tmp=0;

for(i=0;iif(dp[st[i]]>tmp)

else

if(dp[st[i]]==tmp)

}cout

else

}puts("");

for(i=0;i0].size();i++) dfs(can[0][i]),ad(0,can[0][i],inf);

s=n+1,t=n+2;

for(i=0;i<=n;i++)

else

if(ds[i]<0)

}for(;bfs();)

cout

<}

NOI2015 小園丁與老司機

一上午就弄這道題 犯了很多錯誤 比如排序排錯 沒更新啥的。大概是把 y 從大到小dp dp i 表示從 i 向上或斜向上走最多多少點 g i 表示假如 i 是這一層第乙個到達的點從 i 開始走最多多少點 可以在這一層走一遍 同一層內從 i 開始到 j 離開該層的話會先繞到該層端點再到 j 當然也可以...

UOJ132 NOI2015 小園丁與老司機

作者部落格 正解 dp 上下界網路流 解題報告 第一 二問是一起的,dp一遍可以解決。具體而言,f i 記錄到達i的最優值,g i 記錄前驅結點。按y分層,不同層之間直接轉,左上右上的一條直線上的點x y座標的和或者差相等,map儲存最後的值 寫轉入會方便一些 同一層之間有一點麻煩,考慮一定是從乙個...

NOI 2015 壽司晚宴

description 為了慶祝 noi 的成功開幕,主辦方為大家準備了一場壽司晚宴。小 g 和小 w 作為參加 noi 的選手,也被邀請參加了壽司晚宴。在晚宴上,主辦方為大家提供了 n 1 種不同的壽司,編號 1,2,3,n 1,其中第 i 種壽司的美味度為 i 1 即壽司的美味度為從 2 到 n...