[洛谷P3621] [APIO2007] 風鈴
Description
你準備給弟弟 Ike 買一件禮物,但是,Ike 挑選禮物的方式很特別:他只喜歡那些能被他排成有序形狀的東西。
你準備給 Ike 買一個風鈴。風鈴是一種多層的裝飾品,一般掛在天花板上。
每個風鈴都包含一些由豎直線連起來的水平桿。每根桿的兩頭都有線連接,下面或者掛著另一根水平桿,或者掛著一個玩具。下面是一個風鈴的例子:
為了滿足弟弟,你需要選一個滿足下面兩個條件的風鈴:
(1) 所有的玩具都在同一層(也就是說,每個玩具到天花板之間的桿的個數是一樣的)或至多相差一層。
(2) 對於兩個相差一層的玩具,左邊的玩具比右邊的玩具要更靠下一點。
風鈴可以按照下面的規則重新排列:任選一根桿,將桿兩頭的線“交換”。也就是解開一根桿左右兩頭的線,然後將它們綁到桿的另一頭。這個操作不會改變更下面的桿上線的排列順序。正在訓練信息學奧林匹克的你,決定設計一個算法,判斷能否通過重新排列,將一個給定的風鈴變為 Ike 喜歡的樣子。
考慮上面的例子,上圖中的風鈴滿足條件(1),卻不滿足條件(2)——最左邊的那個玩具比它右邊的要高。
但是,我們可以通過下面的步驟把這個風鈴變成一個 Ike 喜歡的:
第一步,將桿 1 的左右兩邊交換,這使得桿 2 和桿 3 的位置互換,交換的結果如下圖所示:
第二步,也是最後一步,將桿 2 的左右兩邊交換,這使得桿 4 到了左邊,原來在左邊的玩具到了右邊,交換的結果發下圖所示:
現在的這個風鈴就滿足 Ike 的條件了。
你的任務是:給定一個風鈴的描述,求出最少需要多少次交換才能使這風鈴滿足 Ike 的條件(如果可能)
Input
輸入的第一行包含一個整數 n(1≤n≤100 000),表示風鈴中有多少根桿。
接下來的 n 行描述桿的連接信息。這部分的第 i 行包含兩個由空格分隔的整數 li和 ri,描述桿 i 的左右兩邊懸掛的東西。如果掛的是一個玩具,則對應的值為-1,否則為掛在下面的桿的編號
Output
輸出僅包含一個整數。表示最少需要多少次交換能使風鈴滿足 Ike 的條件。如果不可能滿足,輸出-1。
Sample Input
6
2 3
-1 4
5 6
-1 -1
-1 -1
-1 -1
Sample Output
2
想法
才不會說我是看這個題目好玩才去做的呢
這個題目讓我想起了林清玄的散文《風鈴》:
有了風鈴,風雖然吹過了,還留下美妙的聲音
有了心的風鈴,生命即使走過了,也會留下動人的痕跡
每一次起風的時候,每一步歲月的腳步,都會那樣真實地存在。
等等,跑題了!
這個題就是樹形dp嘛,註意判斷幾種不行的情況:
1.風鈴相差層數>1
2.在滿足風鈴相差層數為1的情況下,把在上面一層的風鈴統稱為F,把下面一層的風鈴統稱為G
在某一節點,其兩個子節點中都既有F又有G
然後註意各種細節就好了(自古樹形dp細節多qwq)
代碼
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100005;
int ch[N][2],pa[N],dep[N];
int n,rt;
int m,d[N],t;
void get_dep(int u){
for(int i=0;i<2;i++){
if(ch[u][i]!=-1){
dep[ch[u][i]]=dep[u]+1;
get_dep(ch[u][i]);
}
else d[m++]=dep[u]+1,t=max(t,d[m-1]);
}
}
int sz[N],num[N]; //num表示該節點子樹中F與G的總和,sz表示該節點子樹中F個數
void dfs0(int u){
for(int i=0;i<2;i++){
if(ch[u][i]!=-1){
dfs0(ch[u][i]);
num[u]+=num[ch[u][i]]; sz[u]+=sz[ch[u][i]];
}
else {
num[u]++;
if(dep[u]+1==t-1) sz[u]++;
}
}
}
int flag;
int dfs(int u){
if(sz[u]==0 || sz[u]==num[u]) return 0;
if(ch[u][0]!=-1 && ch[u][1]!=-1){
if(sz[ch[u][0]]==0) return dfs(ch[u][1]);
if(sz[ch[u][1]]==0) return dfs(ch[u][0])+1;
if(sz[ch[u][0]]==num[ch[u][0]]) return dfs(ch[u][1])+1;
if(sz[ch[u][1]]==num[ch[u][1]]) return dfs(ch[u][0]);
flag=0; return 0;
}
if(ch[u][0]==-1 && ch[u][1]!=-1){
if(sz[ch[u][1]]==0) return 1;
return dfs(ch[u][1])+1;
}
if(ch[u][0]!=-1 && ch[u][1]==-1){
if(sz[ch[u][0]]==0) return 0;
return dfs(ch[u][0]);
}
return 0;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&ch[i][0],&ch[i][1]);
if(ch[i][0]!=-1) pa[ch[i][0]]=i;
if(ch[i][1]!=-1) pa[ch[i][1]]=i;
}
for(int i=1;i<=n;i++) if(!pa[i]) { rt=i; break; }
dep[rt]=1; get_dep(rt);
flag=1;
for(int i=0;i<m;i++) if(d[i]<t-1) flag=0;
if(flag==0) { printf("-1"); return 0; }
dfs0(rt);
if(sz[rt]==0) { printf("0"); return 0; }
int ans=dfs(rt);
if(flag==0) printf("-1");
else printf("%d",ans);
return 0;
}
[洛谷P3621] [APIO2007] 風鈴