[背景]
給定一顆樹(多叉),詢問按照邊(點)的掌管原則,控制次數需要的最小代價
按照邊 :
即對於每條邊的兩個點,至少有一個點花費代價,詢問最小代價
[分析]
令f[i]代表在第i節點花費代價、看管以i爲根的子樹的最小代價,g[i]代表i節點不花費
代價,顯然f[x] =∑ min(f[y],g[y])+1、g[x]=∑f[y]其實很好理解,如果i節點放置,
則其兒子節點無所謂,如果i節點不放置,則其兒子節點必須放置
按照點 :
即對於直接相連的兩個點,至少有一個點花費代價,詢問最小代價
[分析]
寫轉移方程要有切入點,或者
按照一個維度去討論,在這裏我們按照x節點有誰掌管來討論:
1.由自己看管,顯然兒子節點什麼狀態無所謂
2.由兒子節點看管,那麼兒子節點中至少一個自己看管自己,
???還有其他可能嗎
3.由父親看管(很容易漏的),因爲感覺1、2已經包含3這種情況了, 但是試想一下,
在x節點自己掌控自己的話,y作爲x的兒子節點,狀態隨意。可以自己看管自己,可
以由兒子節點看管(需要保證兒子節點至少一個保存的爲f[y])、或者由父親節點看
管(可以全爲g[y])
f[x] =∑ min(f[y],g[y],h[y])+1、
g[x]=∑min(f[y],g[y])+delta;delta=max(min(f[y]-g[y]),0)
h[x]=∑ min(f[y],g[y])
#include<cstdio>
#include<iostream>
using namespace std;
const int N = 1500 + 5;
const int inf = 1e8;
int s[N][N], num[N], du[N], f[N], g[N], h[N];
int n;
void dfs(int x)
{
if (num[x] == 0)
{
f[x] = 1;
g[x] = inf;
h[x] = 0;
return ;
}
int y = s[x][1];
int delta = inf;
for (int i = 1; i <= num[x]; i++)
{
y = s[x][i];
dfs(y);
f[x] += min(min(f[y], g[y]), h[y]);
g[x] += min(f[y], g[y]);
h[x] += min(f[y], g[y]);
delta = min(delta, f[y] - g[y]);
}
f[x]++;
if (delta > 0 && delta != inf)
g[x] += delta;
printf ("%d : (%d, %d)\n",x,f[x],g[x]);
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
int k;
scanf("%d", &k);
scanf("%d", &num[k]);
for (int i = 1; i <= num[k]; i++)
{
scanf("%d", &s[k][i]);
du[s[k][i]]++;
}
}
for (int i = 0; i < n; i++)
if (du[i] == 0)
{
dfs(i);
printf("%d", min(f[i],g[i]));
break;
}
return 0;
}
後記
好久沒打dp了,(果然手生了不少,以後推出方程後,要想找幾種極端情況檢驗一
下,避免狀態漏掉