http://blog.sina.com.cn/s/blog_6bddecdc0102uy9g.html
C 树形DP hdu4863
problem
1.一棵无根树。最多50个case,每个case最多200个点。
2.每个点有个value,去掉这个点之后剩下一或多个树,节点数最多的那颗树的节点数,就这这个点的value。
3.value最小的那个点,是整棵树的质心。
4.a.只有一个质心。b.有两个质心并且挨着。c.不会有其他情况。(题目中给的)
5.这棵无根树中,有几颗子树,其质心与正棵树的质心完全相同。
think
我是几乎完全按照官方题解写的。
1.搞出每个点的value从而得到质心。这就不多说了。
2.dp[s][j] 表示,以质点为整棵树的根(如果有两个质点就分成两棵树,具体下面说),s为子树的根,这颗子树有j个节点,的子树个数。
3.那么dp可以用背包得到。公式就是dp[s][j+k] += dp[s][j] * dp[ss][k]; 其中ss是s的孩子,j和k分别表示子树s和子树ss的节点个数。
for(int j = n; j >= 1; --j) if(dp[s][j]){
for(int k = 1; k <= n; ++k){
dp[s][j+k] += dp[s][j] * dp[ss][k];
dp[s][j+k] %= mod;
}
}
4.如果是两个质点,那么所求子树也是相同的这两个质点,只要两边的节点个数一样多就可以啦。A和B分别表示两个质点。
for(int i = 1; i <= n/2; ++i){
ans = (ans + dp[A][i] * dp[B][i]) % mod;
}
5.如果只有一个质点。就用总的方案树减去不合理的方案数。总的方案数是:
for(int j = 1; j <= n; ++j){
ans = (ans + dp[A][j]) % mod;
}
6.不合理的方案的冲要条件是。最大的那个儿子子树(与根直接相连的分子树)的节点数占到了整个子树的节点数的一半甚至更多。这个想一下,就是的。
7.于是不合理的方案数也和上面一样背包处理。这里先枚举那个最大分子树是哪个节点,然后背包处理除了这个子树的类似dp[A][x]的值,由于这里只找A(质点)的所以一维就可以啦,用dp2表示。然后枚举那个不可以的子树的节点个数mx,和dp2[1到mx],他俩相乘就是不可以的。这部分的代码就是solve()函数里面的。
code
const int N = 222;
const int mod = 10007;
vector<int>v[N];
int sz[N];
int val[N];
int dp[N][N];
int dp2[N];
int A, B, n;
void dfs1(int s, int pre){
sz[s] = 1;
int len = v[s].size();
for(int i = 0; i < len; ++i){
int ss = v[s][i];
if(ss == pre) continue;
dfs1(ss, s);
sz[s] += sz[ss];
}
}
void dfs2(int s, int pre){
int len = v[s].size();
val[s] = n - sz[s];
for(int i = 0; i < len; ++i){
int ss = v[s][i];
if(ss == pre) continue;
val[s] = max(val[s], sz[ss]);
dfs2(ss, s);
}
}
void find(){
int tmp = n+10;
A = 0, B = 0;
for(int i = 1; i <= n; ++i){
if(val[i] < tmp){
A = i;
B = 0;
tmp = val[i];
} else if(val[i] == tmp){
B = i;
}
}
}
void dfs3(int s, int p1, int p2){
int len = v[s].size();
dp[s][1] = 1;
for(int i = 0; i < len; ++i){
int ss = v[s][i];
if(ss == p1 || ss == p2) continue;
dfs3(ss, s, p2);
for(int j = n; j >= 1; --j) if(dp[s][j]){
for(int k = 1; k <= n; ++k){
dp[s][j+k] += dp[s][j] * dp[ss][k];
dp[s][j+k] %= mod;
}
}
}
}
int solve(){
int ans = 0;
for(int j = 1; j <= n; ++j){
ans = (ans + dp[A][j]) % mod;
}
int len = v[A].size();
for(int i = 0; i < len; ++i){
int s1 = v[A][i];
memset(dp2, 0, sizeof(dp2));
dp2[1] = 1;
for(int j = 0; j < len; ++j) if(i != j){
int s2 = v[A][j];
for(int h = n; h > 0; --h) if(dp2[h]){
for(int k = 1; k <= n; ++k){
dp2[h+k] += dp2[h] * dp[s2][k];
dp2[h+k] %= mod;
}
}
}
int tmp = 0;
for(int mx = 1; mx <= n; ++mx){
for(int j = 1; j <= mx; ++j){
tmp = (tmp + dp2[j]*dp[s1][mx]) % mod;
}
}
ans = (ans - tmp + mod) % mod;
}
return ans;
}
int main(){
int T, tt = 0;
scanf("%d", &T);
while(T--){
scanf("%d", &n);
for(int i = 1; i < n; ++i){
int a, b;
scanf("%d%d", &a, &b);
v[a].PB(b);
v[b].PB(a);
}
dfs1(1, 0); //得到每个子树的size
dfs2(1, 0); //得到每个节点题目中描述的value
find(); //找到质心
int ans = 0;
if(B){ //有两个质心
dfs3(A, 0, B);
dfs3(B, 0, A);
for(int i = 1; i <= n/2; ++i){
ans = (ans + dp[A][i] * dp[B][i]) % mod;
}
} else { //一个质心
dfs3(A, 0, 0);
ans = solve();
}
printf("Case %d: %d\n", ++tt, ans);
for(int i = 1; i <= n; ++i) v[i].clear();
memset(dp, 0, sizeof(dp));
}
return 0;
}
E - Peter's Hobby
problem
http://acm.hdu.edu.cn/showproblem.php?pid=4865
给你每天的叶子状态序列(4种状态),求天气状态序列(3种天气)。
已知前一天天气对后一天天气的影响,和当天天气对叶子的影响,和第一天的天气概率。
think
概率DP。
weather[i][j] 表示前一天天气是i,后一天天气是j的概率,这是题中给的矩阵。
同理leaves[i][j] 表示天气i得到叶子j的概率,和 start[i] 第一天天气是i的概率。
dp[i][j] 表示第i天天气是j的概率。
那么 dp[i][j] = max(k)(dp[i-1][k] * weather[k][j] * leaves[j][a[i]])
其中dp[1][j] = start[j] * leaves[j][a[1]]
hint,用log变成加法,因为小数一直乘下去太小了。
code
double weather[3][3] = {
{0.5, 0.375, 0.125},
{0.25, 0.125, 0.625},
{0.25, 0.375, 0.375} };
double leaves[3][4] = {
{0.6, 0.2, 0.15, 0.05},
{0.25, 0.3, 0.2, 0.25},
{0.05, 0.1, 0.35, 0.5} };
double start[3] = {0.63, 0.17, 0.2};
double dp[55][3];
int path[55][3];
int a[55];
inline int in(){
char str[10];
scanf("%s", str);
int len = strlen(str);
if(len == 3) return 0;
if(len == 6) return 1;
if(len == 4) return 2;
return 3;
}
inline void out(int x){
if(x == 0) puts("Sunny");
else if(x == 1) puts("Cloudy");
else puts("Rainy");
}
void solve(int now, int n){
if(n > 1) solve(path[n][now], n - 1);
out(now);
}
int main(){
int T, tt = 0, n;
for(int i = 0; i < 3; ++i) for(int j = 0; j < 3; ++j) weather[i][j] = log(weather[i][j]);
for(int i = 0; i < 3; ++i) for(int j = 0; j < 4; ++j) leaves[i][j] = log(leaves[i][j]);
for(int i = 0; i < 3; ++i) start[i] = log(start[i]);
scanf("%d", &T);
while(T--){
scanf("%d", &n);
for(int i = 1; i <= n; ++i) a[i] = in();
for(int i = 0; i < 3; ++i) dp[1][i] = start[i] + leaves[i][a[1]];
for(int i = 2; i <= n; ++i){
for(int j = 0; j < 3; ++j){
for(int k = 0; k < 3; ++k){
double tmp = dp[i-1][k] + weather[k][j] + leaves[j][a[i]];
if(k == 0 || tmp > dp[i][j]){
dp[i][j] = tmp;
path[i][j] = k;
}
}
}
}
int now = 0;
double tmp = dp[n][0];
for(int i = 1; i < 3; ++i){
if(dp[n][i] > tmp){
now = i;
tmp = dp[n][i];
}
}
printf("Case #%d:\n", ++tt);
solve(now, n);
}
return 0;
}