题目:
题目分析:
状态方程: dp[当前节点的标号][当前已经选取的城市数]
设已经选取的城市数是K
初始状态: dp[u][0] = dp[u][1] = 0 , 其他的将值设置为无穷大
树形转移: dp[father][k] = min ( dp[father][k] , dp[father][k1] + dp[son][k-k1] + k1 *( k-k1)*weight_of_edge );
讲解: 给出的点组成了一个树形结构,而且是求取最优解,所以我们可以运用动态规划,方程存的值是当前状态下最小的子情况,我们可以利用分组背包进行状态转移,把每个子树看做一组物品,每组物品中只有已经选取的城市数不同,每件物品的花费是当前路径在规划中的总花费,这条边下面子树选取了k1个点,这条边另一侧要选取k-k1个点,所以一定有且仅有k1*(k-k1)t条路径经过这条边,所以花费可得
代码如下,不懂可以在评论中提问,当天回复
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAX 4007
using namespace std;
struct
{
int v,next,w;
}e[MAX];
int t,k,n;
int head[MAX];
long long dp[MAX][57];
int cc;
void add ( int u , int v , int w )
{
e[cc].v = v;
e[cc].w = w;
e[cc].next = head[u];
head[u] = cc++;
}
void dfs ( int u , int fa )
{
dp[u][0] = 0;
dp[u][1] = 0;
for ( int i = head[u] ; i != -1 ; i = e[i].next )
{
int v = e[i].v;
if ( v == fa ) continue;
dfs ( v , u );
for ( int j = k ; j >= 0 ; j-- )
{
for ( int t = 1 ; t <= j ; t++ )
dp[u][j] = min ( dp[u][j] , dp[u][j-t] + (long long) dp[v][t] + (long long)(t*(k-t)* e[i].w ) );
}
}
}
int main ( )
{
int a ,b , c ;
scanf ( "%d" , &t );
while ( t-- )
{
scanf ( "%d%d" , &n , &k );
memset ( head , -1 , sizeof ( head ) );
cc = 0;
for ( int i = 1 ; i < n ; i++ )
{
scanf ( "%d%d%d" , &a , &b , &c );
add ( a , b ,c );
add ( b , a ,c );
}
memset ( dp , 0x3f , sizeof ( dp ) );
dfs ( 1 , -1 );
printf ( "%lld\n" , dp[1][k]*2 );
}
}