[CSP-S模擬測試]:w(樹上DP)

題目背景

$\frac{1}{4}$遇到了一道水題,雙徹底不會作,因而去請教小$D$。小$D$看了${0.607}^2$眼就切掉了這題,嘲諷了$\frac{1}{4}$一番就離開了。因而,$\frac{1}{4}$只好來問你,這道題是這樣的:html


題目描述

有一棵$n$個節點的樹,每條邊長度爲$1$,顏色爲黑或白。
能夠執行若干次以下操做:選擇一條簡單路徑,反轉路徑上全部邊的顏色。
對於某些邊,要求在操做結束時爲某一種顏色。
給定每條邊的初始顏色,求最小操做數,以及知足操做數最小時,最小的操做路徑長度和。
c++


輸入格式

從文件$w.in$中讀入數據。
第一行,一個正整數$n$。
接下來$n−1$行,每行四個整數$a,b,c,d$:
$\bullet$樹中有一條邊鏈接$a$和$b$。
$\bullet c=0,1$表示初始顏色爲白色、黑色。
$\bullet d=0,1,2$表示最終要求爲白色、要求爲黑色、沒有要求。
spa


輸出格式

輸出到文件$w.out$中。
輸出一行,兩個整數,表示最小操做數、操做數最小時的最小路徑長度和。
htm


樣例

樣例輸入1:blog

5
2 1 1 0
1 3 0 1
2 4 1 2
5 2 1 1
get

樣例輸出1:it

1 2io

樣例輸入2:class

3
1 3 1 2
2 1 0 0
im

樣例輸出2:

0 0

樣例輸入3:

6
1 3 0 1
1 2 0 2
2 4 1 0
4 5 1 0
5 6 0 2

樣例輸出3:

1 4


數據範圍與提示

樣例$1$解釋:

操做路徑$\{2,1,3\}$。

數據範圍:

保證給出的圖爲一棵樹,有$n\in [1,10^5],a,b\in [1,n],c\in \{0,1\},d\in\{0,1,2\}$。


題解

看到了這道題,我就像到了前一陣作過的一道題:

而後我就死了……

那道題能夠用貪心解決,可是這道題咱們還須要回答最小的操做路徑長度和……

不過有一些貪心策略仍是能夠利用一下的:

  $\alpha.$每一條邊不可能被翻轉兩次以上。

  $\beta.$對於一個邊集$E$,咱們所須要翻轉的次數爲這個邊集中奇數點的個數除$2$。

這道題要求咱們在保證第一問最小的狀況下儘量保證第二問最小,因此這很難搞(竟然連部分分都沒有,傷心……)。

如今咱們來考慮如何求出第二問,首先,定義$dp[i][0/1]$表示鏈接$i$的邊是否翻轉的最小代價(爲方便,如下用二元組$pair<int,int>$分別表示第一問和第二問)。

如今考慮如何將兒子轉移給父親,設$w_1$表示已經轉移的兒子中有一條與當前節點的連邊翻轉了的最小代價,$w_2$則表示尚未這樣的一條邊,則:

$$w_1=\min(w_1+dp[v][0],w_2+dp[v][1])$$

$$w_2=\min(w_1+dp[v][1],w_2+dp[v][0])$$

初始值:$w_1=(inf,inf),w_2=(0,0)$。

那麼,如今咱們已經求出了$w_1$和$w_2$,在來考慮如何更新$dp$中的答案。

這時候分兩種狀況:

  $\alpha.$頭頂的邊必定要翻轉:

$$dp[i][0]=(inf,inf)$$

$$dp[i][1]=\min((w1.first,w1.second+1),(w2.first+1,w2.second+1))$$

  $\beta.$頭頂的邊不須要要翻轉:

$$dp[i][0]=\min((w1.first+1,w1.second),w2)$$

$$dp[i][1]=(inf,inf)$$

最後的答案就是,第一問:$\dfrac{dp[i][0].first}{2}$;第二問:$dp[i][0].second$。

時間複雜度:$\Theta(n)$。

指望得分:$100$分。

實際得分:$100$分。


代碼時刻

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
struct rec{int nxt,to,w;}e[200001];
int n;
int head[100001],cnt;
pair<int,int> dp[2][100001];
void add(int x,int y,int w)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	e[cnt].w=w;
	head[x]=cnt;
}
pair<int,int> pls(pair<int,int> a,pair<int,int> b){return make_pair(a.first+b.first,a.second+b.second);}
pair<int,int> min(pair<int,int> a,pair<int,int> b){return(a.first==b.first)?((a.second<b.second)?a:b):((a.first<b.first)?a:b);}
void dfs(int x,int fa,int w)
{
	pair<int,int> w1=make_pair(inf,inf);
	pair<int,int> w2=make_pair(0,0);
	for(int i=head[x];i;i=e[i].nxt)
		if(e[i].to!=fa)
		{
			dfs(e[i].to,x,e[i].w);
			pair<int,int> flag1=min(pls(w1,dp[0][e[i].to]),pls(w2,dp[1][e[i].to]));
			pair<int,int> flag2=min(pls(w1,dp[1][e[i].to]),pls(w2,dp[0][e[i].to]));
			w1=flag1;
			w2=flag2;
		}
	if(w==1)dp[0][x]=make_pair(inf,inf);
	else dp[0][x]=min(make_pair(w1.first+1,w1.second),w2);
	if(!w)dp[1][x]=make_pair(inf,inf);
	else dp[1][x]=min(make_pair(w1.first,w1.second+1),make_pair(w2.first+1,w2.second+1));
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		if(d==2){add(a,b,2);add(b,a,2);}
		else {add(a,b,c!=d);add(b,a,c!=d);}
	}
	dfs(1,0,2);
	printf("%d %d\n",dp[0][1].first>>1,dp[0][1].second);
	return 0;
}

rp++

相關文章
相關標籤/搜索