[背景]
给定一颗树(多叉),询问按照边(点)的掌管原则,控制次数需要的最小代价
按照边 :
即对于每条边的两个点,至少有一个点花费代价,询问最小代价
[分析]
令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了,(果然手生了不少,以后推出方程后,要想找几种极端情况检验一
下,避免状态漏掉