HDU1011——Starship Troopers(树形DP),HDU1012——u Calculate e,HDU1013——Digital Roots

目录

HDU1011——Starship Troopers(树形DP)

题目描述

运行代码

代码思路

树形DP

HDU1012——u Calculate e

题目描述

运行代码

代码思路

HDU1013——Digital Roots

题目描述

超时代码

改进后依旧超时代码

运行代码

代码思路

HDU1011——Starship Troopers(树形DP)

题目描述

Problem - 1011

运行代码

#include <iostream>
#include <vector>

using namespace std;

const int MAXN = 110;
int n, m;
int cost[MAXN], weight[MAXN];
int dp[MAXN][MAXN];
bool visited[MAXN];
vector<int> tree[MAXN];  // 建树

// 计算向上取整的除法结果
int ceilDiv(int num, int div) {
    return (num + div - 1) / div;
}

// 比较两个整数并返回最大值
int maxValue(int a, int b) {
    return a > b ? a : b;
}

// 深度优先搜索函数
void dfs(int p) {
    int i, j, k;
    int temp = ceilDiv(cost[p], 20);  // 攻打 p 至少需要 temp 个人

    for (i = temp; i <= m; i++)
        dp[p][i] = weight[p];

    visited[p] = true;  // 记录下已经处理 p 节点, 强制只能顺着树往下撸

    for (i = 0; i < tree[p].size(); i++) {
        int t = tree[p][i];
        if (visited[t])
            continue;

        dfs(t);
        for (j = m; j > temp; j--) {  // 树形背包, 容量从后向前推
            for (k = 1; k <= j - temp; k++)  // 留下 j - k 攻打 p, k 攻打儿子
                dp[p][j] = maxValue(dp[p][j], dp[p][j - k] + dp[t][k]);
        }
    }
}

int main() {
    int i;
    while (cin >> n >> m, n != -1 || m != -1) {
        for (i = 0; i <= n; i++)
            tree[i].clear();

        memset(dp, 0, sizeof(dp));
        memset(visited, false, sizeof(visited));

        for (i = 1; i <= n; i++)
            cin >> cost[i] >> weight[i];

        for (i = 1; i < n; i++) {
            int u, v;
            cin >> u >> v;
            tree[u].push_back(v);
            tree[v].push_back(u);
        }

        if (m == 0) {
            cout << "0" << endl;
            continue;
        }

        dfs(1);
        cout << dp[1][m] << endl;
    }

    return 0;
}

代码思路

  1. 初始化:

    • MAXN定义数组的最大尺寸。
    • nm分别代表房间数和星际战士的数量。
    • cost[]weight[]数组分别存储每个房间内虫子的数量(访问成本)和是否可能包含大脑(1表示可能包含,0表示不包含)。
    • dp[][]是一个二维数组,用于动态规划,存储以一定数量的星际战士在每个房间能访问到的可能含大脑的房间的最大数量。
    • visited[]用于记录哪些房间已经被访问过。
    • tree[][]存储树结构,即每个房间连接的其他房间。
  2. 辅助函数:

    • ceilDiv()函数计算向上取整的除法结果,但在这个场景下,它实际上不需要,因为访问成本是固定的,直接比较即可。
    • maxValue()函数返回两个整数中的较大者。
  3. 深度优先搜索(DFS):

    • dfs()函数递归地遍历树结构。
    • 计算访问当前房间至少需要的星际战士数量。
    • 如果星际战士数量足够,初始化dp数组的相应位置为该房间可能含大脑的权重。
    • 标记当前房间为已访问。
    • 对于当前房间的所有子房间,递归调用dfs()
    • 在所有子房间处理完毕后,使用动态规划更新当前房间的dp值,考虑子房间的贡献。
  4. 主函数:

    • 循环读取输入,直到遇到结束标志(两个-1)。
    • 每次循环前清空树结构和重置dpvisited数组。
    • 读取房间描述和连接信息。
    • 特殊情况处理:如果m为0,则直接输出0。
    • 否则,调用dfs()函数从根节点开始。
    • 输出以m个星际战士在根节点(房间1)所能访问到的可能含大脑的房间的最大数量。

树形DP

树形动态规划(Tree Dynamic Programming,简称树形DP)是一种解决在树形结构上的最优化问题的策略,它结合了图论和动态规划技术。树形DP常用于解决以下类型的问题:

  1. 路径问题:如求树中两点间最长或最短路径、路径上的最大权值等。
  2. 子树问题:如求具有特定性质的子树的最大或最小值,或者求子树中的某些统计信息。
  3. 覆盖问题:如用最少的节点覆盖树中的所有节点,或者达到某种条件下的最佳覆盖方案。

树形DP的关键步骤包括:

  • 状态定义:确定DP状态,这通常涉及到子树的概念,比如dp[node][state]表示以node为根的子树在某种状态下的最优解。
  • 状态转移:在状态定义的基础上,找到父节点状态和子节点状态之间的关系,从而实现状态的传递。
  • 初始化:确定DP数组的初始状态,这通常是叶子节点或边界条件。
  • 计算顺序:由于树形DP依赖于子问题的解,所以通常采用深度优先搜索(DFS)或广度优先搜索(BFS)遍历树,确保总是先解决子问题再解决父问题。
  • 最终答案:确定最终问题的答案对应DP数组中的哪个状态。

树形DP的伪代码示例,用于解决一个关于子树最大值的问题:

def dfs(node):
    # 初始化dp[node]为某个初始值
    dp[node] = initial_value

    # 遍历当前节点的所有子节点
    for child in adj_list[node]:
        # 递归地解决子节点问题
        dfs(child)

        # 状态转移:更新当前节点的dp值
        dp[node] = max_function(dp[node], dp[child])

# 主函数中调用dfs函数从根节点开始
dfs(root)

adj_list是一个邻接表,用于表示树的结构;initial_value是根据具体问题设定的初始值;max_function是一个函数,用于根据子节点的状态更新当前节点的状态,这通常涉及到某种最优化操作。

HDU1012——u Calculate e

题目描述

Problem - 1012

运行代码

#include <iostream>
#include <iomanip>
using namespace std;
int factor(int n) {
    int result = 1;
    for (int i = 1; i <= n; i++) {
        result *= i;
    }
    return result;
}

int main() {
    cout << "n " << "e" << endl;
   cout << "- " << "-----------" << endl;
   printf("0 1\n");
   printf("1 2\n");
   printf("2 2.5\n");
    double e = 2.5;
    for (int n = 3; n <= 9; n++) {
        double term = 1.0 / factor(n);
        e += term;
        cout << n << " " << fixed << setprecision(9) << noshowpoint << e <<endl;
    }
    return 0;
}

代码思路

e可以通过下面的无穷级数来逼近:

  1. 定义factor函数:这个函数计算阶乘n!,即从1n的所有整数的乘积。使用一个循环从1n,将每个数乘到result变量上,最后返回result

  2. 设置输出格式:输出标题行,显示ne。使用cout输出分隔线,增加可读性。

  3. 初始化输出:直接输出e的前几项值(当n=0e=1n=1e=2n=2e=2.5),这是因为这些值可以直接计算,无需调用factor函数。

  4. 计算和输出后续项:使用一个循环从n=3n=9,每次迭代计算泰勒级数的下一项term,它是1除以n!的值。将term加到e的当前值上,以累加泰勒级数的和。使用cout和格式化指令(fixedsetprecisionnoshowpoint)输出n和当前的e的近似值。

HDU1013——Digital Roots

题目描述

Problem - 1013

超时代码

#include <iostream>
using namespace std;
int digit(int num) {
    int sum = 0;
    while (num > 0) {
        sum += num % 10;
        num /= 10;
    }
    if (sum < 10) {
        return sum;
    }
    else {
        return digit(sum);
    }
}

int main() {
    int n;
    cin >> n;
    while (n != 0) {
       cout << digit(n) <<endl;
        cin >> n;
    }
    return 0;
}

改进后依旧超时代码

#include <iostream>
using namespace std;
int digit(int num) {
    return 1 + (num - 1) % 9;
}
int main() {
    int n;
    cin >> n;
    while (n != 0) {
        cout << digit(n) << endl;
        cin >> n;
    }
    return 0;
}

运行代码

#include <iostream>
#include <string>
using namespace std;
int digit(const string& numStr) {
    int num = 0;
    for (char c : numStr) {
        num += (c - '0');
    }
    int temp = num % 9;
    if (temp == 0) {
        return 9;
    }
    return temp;
}

int main() {
    string numStr;
    while (cin >> numStr && numStr[0] != '0') {
       cout << digit(numStr) << endl;
    }
    return 0;
}

代码思路

模9的操作实际上是基于数字根(Digital Root)的概念,即一个数的数字根是将其所有数字相加,重复这个过程直到得到一个单数字的结果,这个结果对于任何数来说都是其各位数字相加之和模9的结果(除了全为9的特殊情况)。

  1. 定义digit函数

    • 函数接受一个字符串numStr作为参数,这个字符串表示一个正整数。
    • 通过遍历字符串中的每一个字符(字符代表数字),将字符转换为对应的整数(通过c - '0')并累加到num变量中。
    • 计算num模9的结果,如果结果为0,说明num能被9整除,函数返回9;否则,返回模9的结果。
  2. main函数

    • 使用一个无限循环读取标准输入中的字符串,直到读入的第一个字符是'0'为止。
    • 在循环内部,每次读取一个字符串numStr
    • 调用digit函数计算该字符串代表的数的数字根,并输出结果。
    • 当读入的字符串以'0'开头时,循环终止,程序结束。
  • 30
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

筱姌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值