日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

poj1947

發(fā)布時間:2023/12/15 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 poj1947 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

樹狀dp,不簡單。

題意:給出一棵有根樹,問最少切斷幾條邊可以得到有i個結(jié)點的子樹(連通分支)。

采用左兒子右兄弟的存儲方式,

dp用兩個數(shù)組:

f[i][j]表示以i為根的子樹,形成以本子樹根結(jié)點為根(不包含本子樹以外結(jié)點),共j個結(jié)點的子樹所需的最小消耗。這里并沒有計算切斷i與其父親連接的邊的消耗。

g[i][j]表示以i節(jié)點的父親為根的,i結(jié)點以及i的父親的子結(jié)點鏈中的i以后的結(jié)點做根的子樹,構(gòu)成共j個結(jié)點的以i的父親為根(不包含本子樹以外結(jié)點)的子樹所需的最小消耗(i的兄弟們在形成子樹時與i的父親相連,即計算消耗時無需切斷與i父親的連接,但j個結(jié)點中不包含i的父親)

有狀態(tài)轉(zhuǎn)移方程:g[root][count] = min(g[tree[root].next][count - i] + f[root][i], g[root][count]);

f[root][count +1] = g[tree[root].son][count];

我們只需要把f[i][0]初始化為1,就可以直接把切斷整個以root為根的子樹的情況融合在狀態(tài)轉(zhuǎn)移方程中了。意思就是在以root為根的子樹中取0個點,需要切斷與root的連接,消耗1。

用遞歸的方法來填寫兩個數(shù)組??梢孕蜗蟮卣J為最外層循環(huán)是枚舉count,對于每個count,對樹進行從下到上,對子樹鏈進行從右到左的填寫。

其實用記憶化搜索的方式會更簡便易懂。

View Code #include <iostream>
#include
<cstdio>
#include
<cstdlib>
#include
<cstring>
usingnamespace std;

#define maxn 155
#define inf 0x3f3f3f3f

struct Node
{
int son, next, father, num;
} tree[maxn];

int n, p, root;
int f[maxn][maxn], g[maxn][maxn];

void work(int root, int count)
{
if (root ==0)
return;
work(tree[root].son, count);
work(tree[root].next, count);
g[root][count]
= inf;
if (tree[root].next)
for (int i =0; i <= count; i++)
g[root][count]
= min(g[tree[root].next][count - i] + f[root][i],
g[root][count]);
else
g[root][count]
= f[root][count];
f[root][count
+1] = inf;
if (count +1> tree[root].num)
return;
f[root][count
+1] = g[tree[root].son][count];
}

void cal(int root)
{
if (root ==0)
return;
cal(tree[root].son);
cal(tree[root].next);
int temp = tree[root].son;
tree[root].num
=1;
while (temp)
{
tree[root].num
+= tree[temp].num;
temp
= tree[temp].next;
}
}

int main()
{
//freopen("t.txt", "r", stdin);
scanf("%d%d", &n, &p);
memset(tree,
0, sizeof(tree));
for (int i =0; i < n -1; i++)
{
int a, b;
scanf(
"%d%d", &a, &b);
tree[b].next
= tree[a].son;
tree[a].son
= b;
tree[b].father
= a;
}
for (int i =1; i <= n; i++)
if (tree[i].father ==0)
{
root
= i;
break;
}
cal(root);
for (int i =1; i <= n; i++)
f[i][
0] =1;
for (int i =0; i < p; i++)
work(root, i);
int ans = inf;
for (int i =1; i <= n; i++)
if (i == root)
ans
= min(ans, f[i][p]);
else
ans
= min(ans, f[i][p] +1);
printf(
"%d\n", ans);
return0;
}

總結(jié)

以上是生活随笔為你收集整理的poj1947的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。