首页 > 代码库 > HDU 5148 Cities
HDU 5148 Cities
题意:
n(2000)个点的树 从中选出k(50)个点 要求 选出的点中等概率选出两个点 使得这两点的期望值最小 输出期望值乘k^2
思路:
我们将题目的要求化简 sum( sum( dis(i,j) ) ) / k^2 * k^2 其实就是求 sum( sum( dis(i,j) ) ) 其中i和j为任意选出的k个点
设dp[i][k]表示扫描完i为根的子树选出k个点的最小sum
那么转移方程就是 dp[father][k1+k2] = min( dp[father][k1]+dp[son][k2]+edge*k2*(k-k2)*2 ) 这个方程很好理解 注意式中的k2 我们利用它计算的原因是 son树内之选k2个点
代码书写的时候还要注意 dp过程中k1需要倒着for 这个做过01背包应该很好理解
代码:
#include<cstdio> #include<iostream> #include<cstring> #include<string> #include<algorithm> #include<map> #include<set> #include<vector> #include<queue> #include<cstdlib> #include<ctime> #include<cmath> using namespace std; typedef long long LL; #define N 2010 #define M 60 inline void scand(int &ret) { char c; ret = 0; while ((c = getchar()) < '0' || c > '9') ; while (c >= '0' && c <= '9') ret = ret * 10 + (c - '0'), c = getchar(); } int t, n, m; LL dp[N][M]; LL ans; int head[N], tot; struct edge { int v, w, next; } ed[N * 2]; void add(int u, int v, int w) { ed[tot].v = v; ed[tot].w = w; ed[tot].next = head[u]; head[u] = tot++; } void update(LL &x, LL y) { if (y == -1) return; if (x == -1 || x > y) x = y; } void dfs(int u, int fa) { dp[u][1] = 0; for (int i = head[u]; ~i; i = ed[i].next) { int v = ed[i].v; if (v != fa) { dfs(v, u); LL w = ed[i].w; for (int k1 = m - 1; k1; k1--) { if (dp[u][k1] == -1) continue; for (int k2 = 1; k2 < m; k2++) { if (k2 + k1 > m || dp[v][k2] == -1) break; update(dp[u][k1 + k2], dp[u][k1] + dp[v][k2] + w * (m - k2) * k2 * 2); } } } } update(ans, dp[u][m]); } int main() { scand(t); while (t--) { scand(n); scand(m); memset(head, -1, sizeof (head)); tot = 0; for (int i = 2; i <= n; i++) { int u, v, w; scand(u); scand(v); scand(w); add(u, v, w); add(v, u, w); } memset(dp, -1, sizeof (dp)); ans = -1; dfs(1, 0); printf("%I64d\n", ans); } return 0; }
HDU 5148 Cities
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。