热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ACM: 动态规划题 黑书-…

贪吃的九头蛇【问题描述】传说中的九头龙是一种特别贪吃的动物。虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远

贪吃的九头蛇

【问题描述】

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

有一天,有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

        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
#include
#include
#include
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 }

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     {
        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      


推荐阅读
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 本文介绍了一个程序,可以输出1000内能被3整除且个位数为6的所有整数。程序使用了循环和条件判断语句来筛选符合条件的整数,并将其输出。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
author-avatar
z1452
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有