ACM: 动态规划题 黑书-…

来源:互联网 发布:java酒店管理系统程序 编辑:程序博客网 时间:2024/05/16 16:01

贪吃的九头蛇

【问题描述】

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

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

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

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

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

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

ACM: <wbr>动态规划题 <wbr>黑书-贪吃的九头龙

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


【输入文件】

输入文件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)

                           otherjudge(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;
    intnext;
}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 return0;
}

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));
    }
   
    returndp[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");
       elseprintf("%d\n", DP(son[1], K-1, 0));
    }
    return0;
}

0 0
原创粉丝点击