24-3-12至14 自用

文章介绍了如何用动态规划方法解决计算节点子树和、地图路径计数问题以及括号生成问题,展示了相关代码实现。
摘要由CSDN通过智能技术生成

计算每个节点的子树的和

示例:1 2 4 -1 -1 5 -1 -1 3 6 -1 -1 7 -1 -1所得为

       1
      / \
     2   3
    / \   / \
   4  5 6  7

#include <iostream>
#include <vector>
#include <cstring>

using namespace std;

const int MAX_N = 10001;

vector<int> tree[MAX_N]; // 存储树结构
int sum[MAX_N]; // 存储每个节点的子树和

// 构建树结构
void buildTree(int node) {
    int value;
    cin >> value;
    if (value == -1) return; // 如果节点为空,直接返回
    sum[node] += value; // 将节点值加到节点的子树和中
    buildTree(node - 1); // 递归构建左子树
    buildTree(node + 1); // 递归构建右子树
}

// 初始化树结构
bool init() {
    int value;
    cin >> value;
    if (value == -1) return false; // 如果节点为空,返回 false 表示输入结束
    memset(sum, 0, sizeof(sum)); // 初始化节点的子树和为 0
    int root = MAX_N / 2; // 根节点的索引
    sum[root] = value; // 将根节点的值加到根节点的子树和中
    buildTree(root - 1); // 构建左子树
    buildTree(root + 1); // 构建右子树
    return true;
}

int main() {
    int caseNum = 0;
    while (init()) { // 初始化树结构,直到输入结束
        int node = 0;
        while (sum[node] == 0) node++; // 找到第一个非空节点
        cout << "case " << ++caseNum << ":\n" << sum[node++]; // 输出第一个非空节点的子树和
        while (sum[node] != 0) cout << " " << sum[node++]; // 输出剩余节点的子树和
        cout << "\n\n";
    }
    return 0;
}

建造房屋

问题描述

小蓝和小桥是两位年轻的建筑师,他们正在设计一座新的城
市。

在这个城市中,有N条街道,每条街道上都有M个位置可
以建造房屋(一个位置只能建造一个房屋)。建造一个房屋的
费用为1元,小蓝和小桥共有K 元的建造预算。

现在,他们想知道,一共有多少种建造方案,满足以下要求:

·在每条街道上,至少建一个房屋。
·建造的总成本不能超过K元。

由于方案数可能很大,他们只需要输出答案对109+7取模
的结果。

输入格式

一行三个整数N,M(1≤N,M≤30)和K(1≤
K≤N·M),分别表示街道数、街道的位置数和预算。

输出格式

一个整数,表示满足条件的建造方案数对109+7取模的结
果。

思路:dp[i][j] 表示在前 i 个地块中建造了 j 栋房子的方案数。

#include <iostream>
#include <algorithm>
#include <cstring> // for memset
using namespace std;

const long long p = 1e9+7;

int main(void) {
    int n, m, k;
    cin >> n >> m >> k;
    long long dp[n + 1][m + 1];
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= n; i++) {
        dp[i][1] = 1;
        for (int j = i; j <= k; j++) {
            dp[i][j] += dp[i][j - 1] % p; // 不多建房屋
            for (int a = 1; a < min(m, j); a++) { // 再建房屋
                dp[i][j] += dp[i - 1][j - a] % p; // 此前的方案数累加
            }
        }
    }
    long long ans = 0;
    for (int j = 1; j <= k; j++) {
        ans += dp[n][j] % p;
        ans %= p;
    }
    cout << ans;
    return 0;
}

破损楼梯

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int p = 1e9 + 7;

int main(void){
    int n, m;
    cin >> n >> m;
    int dp[n + 1];
    int broken[m + 1];
    memset(dp, 0, sizeof(dp));
    memset(broken, 0, sizeof(broken));
    int x;
    for(int i = 1; i <= m; i++){
        cin >> x;
        broken[x] = 1;
    }
    dp[0] = 1;
    dp[1] = !broken[1];
    for(int i = 2; i <= n; i++){
        if(broken[i]) {
            dp[i] = 0; // 如果第i个街道被损坏,则不能建造房屋,方案数为0
        } else {
            dp[i] = (dp[i - 1] + dp[i - 2]) % p; // 否则按照动态规划递推关系计算方案数
        }
    }
    cout << dp[n];
}

括号生成

给出括号数,输出所有括号组成方案

最优解
class Solution {
public:
    vector<string> generateParenthesis(int n) {

		if (n == 0) return {};
		if (n == 1) return { "()" };
		vector<vector<string>> dp(n+1);
		dp[0] = { "" };
		dp[1] = { "()" };
		for (int i = 2; i <= n; i++) {//总括号个数
			for (int j = 0; j <i; j++) {//分括号个数
				for (string p : dp[j])//遍历dp[j]中每个字符串
					for (string q : dp[i - j - 1]) {//所有括号减去自行添加的和已经遍历到的括号
						string str = "(" + p + ")" + q;
						dp[i].push_back(str);
					}
			}
		}
		return dp[n];
    }
};
暴力解
class Solution {
bool valid(const string& str){
	int balance=0;
	for(char c:str){
		if(c=='(')++balance;
		else --balance;
		if(balance<0)return false;
	}
	return balance==0;
}
void generate_all(string& current,int n,vector<string>& result){
	if(n==current.size()){
		if(valid(current)){
			result.push_back(current);
		}
		return;
	}//方案可行
	current+='(';
	generate_all(current,n,result);
	current.pop_back();//回到前一个状态
	current += ')';
    generate_all(current, n, result);
    current.pop_back();
}
public:
    vector<string> generateParenthesis(int n) {
		vector<string>result;
		string current;
		generate_all(current,n*2,result);
		return result;
    }
};

地图

问题描述 传说,在蓝桥王国中一个极其神秘的森林。这个森林的起点在(1,1),终点在(n,m)。在你进入这个森岭后,每次你只可以向下或者向右走,由于森岭的神秘力量,至多只可以改变k次方向。 小蓝现在想知道,一共有多少种方案可以从(1,1)进入然后从 (n,m)走出。

数据保证至少存在一种方案。 输入格式 第1行包含三个正整数n,m,k,分别表示地图的规模和可以改变方向的次数。 第2到n+1行包含m个字符,表示地图上该位置的信息,用.表示空地,#表示石头无法通行,保证起点和终点不为石头。 输出格式 输出共1行,包含1个整数,表示方案数。 样例输入 331 .#. 样例输出 2 评测数据规模 对于所有评测数据,1≤n,m≤100,1≤k≤5。 运行限制 语言 最大运行时间 最大运行内存 C++ 3s 512M C 3s 512M Java 5s 512M Python3 10s 512M

思路:与八皇后有点像,因为只能向右向下,不会回到原来的位置,无需记录走过vis。如果要打印路径用f[x][y][d][step]记录位置、方向和步数。

其实我感觉if (f[x][y][d][step]) return f[x][y][d][step];改node,含位置、是否访问过、方向和步数会更好吧,这个不太理解

#include <bits/stdc++.h>
using namespace std;
 
const int N = 101;
int n, m, k;
char s[N][N];
int f[N][N][2][6];
int dx[] = {0, 1}, dy[] = {1, 0}; // 方向数组
 
int dfs(int x, int y, int d, int step) {//xy坐标,d走的方法,step剩余步数
    if (x > n || y > n) return 0; // 越界检查
    if (s[x][y] == '#') return 0; // 障碍物检查
    if (step > k) return 0; // 方向变换次数检查
    if (x == n && y == m) return 1; // 达到终点,return 1代表成功
    if (f[x][y][d][step]) return f[x][y][d][step]; // 记忆化搜索到原来走过的路线直接返回
 
    int res = 0;//res 会累加上从下一步开始找到的满足条件的路径数量。
    for (int i = 0; i < 2; i++) { // 遍历两个方向
        res += dfs(x + dx[i], y + dy[i], i, step + (i != d));//倒数第二次加上最后一次,依次递归
        //step + (i != d)表示下一步的方向变换次数,如果当前方向i和上一步的方向d不同,则方向变换次数加1。
    }
    return f[x][y][d][step] = res;//f[x][y][d][step]记录为了记忆化搜索
}
 
int main() {
    ios::sync_with_stdio(false);//提高性能
    cin.tie(nullptr);//提高性能
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++)
        cin >> s[i] + 1;
 
    int ans = 0;
    if (s[1][2] != '#') ans += dfs(1, 2, 0, 0);//最开始走的两种情况
    if (s[2][1] != '#') ans += dfs(2, 1, 1, 0);
    cout << ans << "\n";
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值