您的位置:首页 > 其它

ACM: 动态规划题 黑书-…

2016-05-19 23:26 274 查看

贪吃的九头蛇

【问题描述】

传说中的九头龙是一种特别贪吃的动物。虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头因衰老而自己脱落。

有一天,有M个脑袋的九头龙看到一棵长有N个果子的果树,喜出望外,恨不得一口把它全部吃掉。可是必须照顾到每个头,因此它需要把N个果子分成M组,每组至少有一个果子,让每个头吃一组。

这M个脑袋中有一个最大,称为“大头”,是众头之首,它要吃掉恰好K个果子,而且K个果子中理所当然地应该包括唯一的一个最大的果子。果子由N-1根树枝连接起来,由于果树是一个整体,因此可以从任意一个果子出发沿着树枝“走到”任何一个其他的果子。

对于每段树枝,如果它所连接的两个果子需要由不同的头来吃掉,那么两个头会共同把树枝弄断而把果子分开;如果这两个果子是由同一个头来吃掉,那么这个头会懒得把它弄断而直接把果子连同树枝一起吃掉。当然,吃树枝并不是很舒服的,因此每段树枝都有一个吃下去的“难受值”,而九头龙的难受值就是所有头吃掉的树枝的“难受值”之和。

九头龙希望它的“难受值”尽量小,你能帮它算算吗?

例如图1所示的例子中,果树包含8个果子,7段树枝,各段树枝的“难受值”标记在了树枝的旁边。九头龙有两个脑袋,大头需要吃掉4个果子,其中必须包含最大的果子。即N=8,M=2,K=4:





图一描述了果树的形态,图二描述了最优策略。

【输入文件】

输入文件dragon.in的第1行包含三个整数N
(1<=N<=300),M
(2<=M<=N),K
(1<=K<=N)。
N个果子依次编号1,2,...,N,且最大的果子的编号总是1。第2行到第N行描述了果树的形态,每行包含三个整数a
(1<=a<=N),b
(1<=b<=N),c
(0<=c<=105),表示存在一段难受值为c的树枝连接果子a和果子b。

【输出文件】

输出文件dragon.out仅有一行,包含一个整数,表示在满足“大头”的要求的前提下,九头龙的难受值的最小值。如果无法满足要求,输出-1。

【样例输入】

8 2 4

1 2 20

1 3 4

1 4 13

2 5 10

2 6 12

3 7 15

3 8 5

8 3 4

1 2 20

1 3 4

1 4 13

2 5 10

2 6 12

3 7 15

3 8 5

2 2 1

1 2 10

2 2 2

1 2 10

【样例输出】

4

0

0

-1

题意: 一条九头龙的动物, 有M个脑袋, 每个脑袋都必须吃到果子, 一棵有N个果子的树, 分配给它每个头吃,

      其中一个最大的头要吃K个果子,
其余分配给其它的头, 如果一个头同时吃到相邻的果子会有一个难受

      值,
现在要你分配果子使得难受值的和最小.

解题思路: (黑书思路)

       
1. 无解情况, 果子不够吃, N<K+M-1

       
2. 当M=2时, 大头要不吃到相邻的要不吃不到. 当M>=3时, 确定大头吃掉之后,
剩下的果子按照果树

          
高度奇偶分配即可. 可以确定问题: 当M=2时, 难受值=两端果子被大头或小头吃的难受值之和.

          
当M>=3时, 两端的果子都被大头吃掉的难受值之和.(因为问题有解,
所以剩下一定满足不相邻分配)

       
3. 为了简化问题(减少决策), 将多叉树转化成二叉树结构(左孩子右兄弟结构).

           
设状态: dp[i][j][k]: 表示以i节点为根的子树有j个果子分配给大头吃的最小难受值. 其中,

           
k=0表示fa[i]被大头吃, k=1表示fa[i]被小头吃.

           
方程:  dp[i][j][k] = min(

                                    dp[X1][j1][1]+dp[X2][j-j1][k]
+ judge(k,1)*cost(i, fa[i]);

                                   
dp[X1][j1][0]+dp[X2][j-j1-1][k] + judge(k,0)*cost(i, fa[i]);

                                           
);

            X1:是i节点的字节点,
X2:是i节点的兄弟节点(父亲被吃情况相同时k); judge判断k与1是否相同.

            judge(k1,
k2): (k1 == 1&&k2 ==
1  ==> judge(k1,k2) = 1)

                           (k1
== 0&&k2 ==
0&&M==2 ==>
judge(k1, k2) == 1)

                           other
judge(k1, k2) == 0;

            边界:
dp(0,0,k) = 0; dp(0,j,k) = INF(无穷大, 表示情况不成立,
j>0);

代码:

#include <cstdio>

#include <iostream>

#include <cstring>

#include <vector>

using namespace std;

#define MAX 305

const int INF = (1<<29);

struct node

{

    int v;

    int
next;

}edges[MAX*2];

int n, m, K;

int dp[MAX][MAX][2];

int fa[MAX], son[MAX], bro[MAX], ch[MAX];

int first[MAX], num;

int cost[MAX][MAX];

inline int min(int a, int b)

{

    return a
< b ? a : b;

}

inline void add(int u, int v)

{

    edges[num].v
= v;

   
edges[num].next = first[u];

    first[u] =
num++;

}

void readGraph()

{

    memset(dp,
-1, sizeof(dp));

   
memset(first, -1, sizeof(first));

    memset(son,
0, sizeof(son));

    memset(bro,
0, sizeof(bro));

    memset(cost,
0, sizeof(cost));

    num =
0;

    for(int i =
1; i <= n; ++i)

    {

   
    ch[i] =
1;

   
    fa[i] =
i;

    }

    int u, v,
w;

    for(int i =
1; i < n; ++i)

    {

   
    scanf("%d %d
%d", &u, &v,
&w);

   
    cost[u][v] =
cost[v][u] = w;

   
    add(u,
v);

   
    add(v,
u);

    }

}

void makeGraph(int u, int f) //转换成二叉树

{

    fa[u] =
f;

    int *point =
&son[u];

    for(int e =
first[u]; e != -1; e = edges[e].next)

    {

   
    int v =
edges[e].v;

   
    if(v == f)
continue;

   
    *point =
v;

   
    point =
&bro[v];

   
    makeGraph(v,
u);

    }

}

inline int judge(int i, int j)

{

    if(i == 1
&& j == 1) return 1;

    else if(i ==
0 && j == 0
&& m == 2) return 1;

    else return
0;

}

int dfs(int x)

{

    if( !son[x]
&& !bro[x] ) return ch[x];

    return ch[x]
+= dfs(son[x])+dfs(bro[x]);

}

int DP(int i, int j, int k)

{

    if(j
< 0) return INF;

   
if(dp[i][j][k] != -1) return dp[i][j][k];

    if(j
> ch[i]) return dp[i][j][k] = INF;

    if(i == 0
&& j == 0) return dp[i][j][k] =
0;

    if(i == 0)
return dp[i][j][k] = INF;

   

    int ans =
INF;

    int temp1 =
INF, temp2 = INF;

    for(int t =
0; t <= j; ++t)

    {

   
    temp1 =
DP(son[i], t, 1)+DP(bro[i], j-t, k)+judge(k,1)*cost[i][fa[i]];
//小头吃i

   
    temp2 =
DP(son[i], t, 0)+DP(bro[i], j-t-1, k)+judge(k,0)*cost[i][fa[i]];
//大头吃i

   
    ans =
min(ans, min(temp1, temp2));

    }

   

    return
dp[i][j][k] = ans;

}

int main()

{

   
freopen("input.txt", "r", stdin);

   
while(scanf("%d %d %d", &n, &m,
&K) != EOF)

    {

   
   
readGraph();

   
    makeGraph(1,
1);

   
   

   
   
dfs(1);

   
    if(n
< K+m-1) printf("-1\n");

   
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: