hdu 5148

来源:互联网 发布:ic卡发卡软件 编辑:程序博客网 时间:2024/05/19 09:51

分配城市

题目描述:

有2000个城市,是一棵树,树上的边有权值,要求选择k~50个城市,使得城市中所有两两的点的距离的总和最小.

题解:

树形dp,考虑用几个点在这棵树上,对于根节点,为了便于好写,强制要求必须选.那么如果能够搞出来,结果就是枚举所有的作为根节点然后取最小的ans,怎么搞呢?先用一种笨的方法:对于k,枚举给当前子树多少个,然后用更新值,这里有个关键点,当前u,子树v为根节点,给子树m个,那么对答案的影响是:dp[v][m]+2*m*(k-m)*边权,因为之后以一个整体数目看v,那么并不知道具体的分布,直接更新完在v树内部对答案的所有贡献. 还有更快的一点:用树形依赖背包的写法.

重点:

关键是dp[u][k]时,算出k个节点在树u上对答案的所有贡献对:就是从边的角度考虑,之后可以将他们看作一个整体

代码:

#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <cmath>#include <ctype.h>#include <limits.h>#include <cstdlib>#include <algorithm>#include <vector>#include <queue>#include <map>#include <stack>#include <set>#include <bitset>#define CLR(a) memset(a, 0, sizeof(a))#define REP(i, a, b) for(ll i = a;i < b;i++)#define REP_D(i, a, b) for(ll i = a;i <= b;i++)typedef long long ll;using namespace std;const ll maxn = 2e3 + 10;const ll INF = 1000000000000ll;ll dp[maxn][60], n, tot;struct info{    ll to, len;};vector<info> G[maxn];void dfs(ll u, ll fa){    dp[u][1] = 0;//一定会选根节点    for(ll i = 2; i <= tot; i++)    {        dp[u][i] = INF;    }    REP(i, 0, G[u].size())    {        ll v = G[u][i].to, len = G[u][i].len;        if(v!=fa)        {            dfs(v, u);            for(ll j = tot; j >= 2; j--)//倒着更新            {                for(ll k = 1; k <= j - 1; k++)//考虑到更新时用j-k,k不能是0,并且k不能是j,至少要留一个.不然不好用,或者之前dp[u][0]置成INF.                {                    if(dp[u][j-k]!=INF&&dp[v][k]!=INF)                        dp[u][j] = min(dp[u][j], dp[u][j-k]+dp[v][k]+2ll*len*k*(tot-k));                }            }        }    }}void solve(){    dfs(1, 0);    ll ans = INF;    for(ll i = 1; i <= n; i++)    {        ans = min(ans, dp[i][tot]);//遍历所有可能的根节点.    }    printf("%I64d\n", ans);}int main(){    //freopen("4Din.txt", "r", stdin);    //freopen("4Dout.txt", "w", stdout);    ll ncase;    scanf("%I64d", &ncase);    while(ncase--)    {        scanf("%I64d%I64d", &n, &tot);        REP_D(i, 1, n)        {            G[i].clear();        }        REP_D(i, 1, n - 1)        {            ll a, b, len;            scanf("%I64d%I64d%I64d", &a, &b, &len);            info t;            t.to = b;            t.len = len;            G[a].push_back(t);            t.to = a;            G[b].push_back(t);        }        solve();    }    return 0;}
0 0