給出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...