jzoj3914. 人品问题 (B组——Day6)

jzoj3914. 人品问题 (B组——Day6)

题目

Description

网上出现了一种高科技产品——人品测试器。只要你把你的真实姓名输入进去,系统将自动输出你的人品指数。yzx不相信自己的人品为0。经过了许多研究后,yzx得出了一个更为科学的人品计算方法。这种方法的理论依据是一个非常重要的结论:人品具有遗传性。因此,一个人的人品完全由他的祖先决定。yzx提出的人品计算方法相当简单,只需要将测试对象的k个祖先的人品指数(可能为负数)加起来即可。选择哪k个祖先可以由测试者自己决定,但必须要满足这个要求:如果除自己的父母之外的某个祖先被选了,那么他的下一代必需要选(不允许跳过某一代选择更远的祖先,否则将失去遗传的意义)。
非常不幸的是,yzx测试了若干次,他的人品值仍然不能为一个正数。现在yzx需要你帮助他找到选择祖先的最优方案,使得他的人品值最大。

Input

第一行是两个用空格隔开的正整数n和k,其中n代表yzx已知的家谱中共有多少人(包括yzx本身在内),k的意义参见问题描述。
第二行有n-1个用空格隔开的整数(可能为负),这些数的绝对值在2^15以内。其中,第i个数表示编号为i+1的人的人品值。我们规定,编号为1的人是yzx。
接下来n行每行有两个用空格隔开的数,其中第i行的两个数分别表示第i个人的父亲和母亲的编号。如果某个人的父亲或母亲不在这个家谱内,则在表示他的父亲或母亲的编号时用0代替。
除yzx以外的所有人都是yzx的祖先,他们都会作为父亲或母亲被描述到。每个人都不可能同时作为多个人的父亲或者是母亲。

Output

一个整数,表示yzx能够得到的最大人品值。

Sample Input

6 3
-2 3 -2 3 -1
2 3
4 5
0 6
0 0
0 0
0 0

Sample Output

4

样例说明:

下图显示了输入样例所描述的家谱图。括号里的数表示的是该人的人品值。

1
4:-2
2:-2
5:3
6:-1
3:3

显然,选择祖先2、3、5能使yzx的人品值达到最大。这个最大值为4,表示yzx能够得到的最大人品值。

Data Constraint

50%的数据,n<=10。
100%的数据,n<=100。

解析

树形dp

代码:

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

int n,k;
int dp[105][105];
bool b[105][105];
 
struct Point{
	int w,l,r;
}f[105];
 
int dfs(int now,int sum){
	if(b[now][sum])	return dp[now][sum];
	if(!sum) return dp[now][sum]=0;
	if(!now) return dp[now][sum]=-10000000;
	if(sum==1) return dp[now][sum]=f[now].w;
	int ans=-10000000;
	for(-int i=0;i<sum;i++) {
		int m=f[now].w+dfs(f[now].l,i)+dfs(f[now].r,sum-i-1);
		ans=max(ans,m);
	}
	b[now][sum]=1;
	dp[now][sum]=ans;
	return ans;
}
 
int main(){
	scanf("%d%d",&n,&k);
	for(int i=2;i<=n;i++)
		scanf("%d",&f[i].w);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&f[i].l,&f[i].r);
	printf("%d",dfs(1,k+1));
   return 0;
}

我们已经知道: - **预期输出**:`1657` - **实际输出**:`1665` 说明当前代码多统计了 $1665 - 1657 = 8$ 个单词。 这意味着在文本处理过程中,有 **8 个不应被当作有效单词的字符串被错误地保留了下来**,或者 **某些符号未正确替换为空格导致本应合并的词被拆成多个词**。 --- 我们要重新审视问题要求: > 不包括空格和**除单引号以外的标点符号**(!"#$%&()*+,./:;<=>?@[]^_{|}~\n) > 这些符号和空白符一样都是单词的分隔符 > 数字及多个连续的数字按一个单词计算 > 类似 I'll 和 wife's 等单词当成一个词计 关键点: - 只允许:字母(a-z, A-Z)、数字(0-9)、以及用于缩写的 `'` - 特别注意:不能让 `'` 出现在词首或词尾,比如 `'s`、`'t`、`'re` 被单独切出来就会变成无效单词但依然计入 → 这可能是问题所在! --- ### ❗问题分析:单引号孤立情况 当前正则表达式:`[^a-zA-Z0-9']` 替换为 空格 → 它保留了所有字母、数字和 `'` 但是这会导致如下情况: 原文可能含有: ``` "It's a beautiful day," said the old man. ``` 经过 `re.sub(r"[^a-zA-Z0-9']", ' ', ...)` 后变成: ``` It's a beautiful day said the old man ``` 然后 `.split()` 得到: ```python ['It's', 'a', 'beautiful', 'day', 'said', 'the', 'old', 'man'] ``` 看起来没问题? 但如果是: ``` "Don't do that!" he shouted. ``` → 处理后:` Don't do that he shouted ` → 分词:`["Don't", "do", "that", "he", "shouted"]` ✅ 但如果出现撇号独立出现,例如: ``` The ’80s were wild. He’s back. She said ‘no’. ``` 注意这里的引号可能是全角、方向性引号(如 `‘`, `’`),它们不是 ASCII 的 `'`(U+0027),而是 Unicode 的 LEFT/RIGHT SINGLE QUOTATION MARK(U+2018/U+2019)。 这些字符在我们的正则表达式中没有被过滤,因为它们不在 `[^a-zA-Z0-9']` 的排除范围内?错! 实际上它们**也不属于 `a-zA-Z0-9'`**,所以应该被替换为空格 → 是安全的。 但我们必须确认:是否这些字符被正确识别? 更危险的是: ### ❌ 错误情况:孤立的撇号 `'` 导致无效单词 例如: ``` I can't believe it's already over. ``` → 正确分词:`['I', 'can't', 'believe', 'it's', 'already', 'over']` ✅ 但如果原文有: ``` He said 'no' and left. ``` → 经过清洗后:`He said 'no' and left` → `.split()` → `['He', 'said', "'no'", 'and', 'left']` 此时 `'no'` 左右带空格,中间是一个由单引号包围的单词,变成了 `"'no'"`,但 Python 字符串里它还是合法的 token。 然而更严重的问题出现在这种情形: ``` word1 ' word2 ``` → 清洗后:`word1 ' word2` → split → ['word1', "'", 'word2'] → 这里的单独 `'` 被当成了一个单词! 这就导致了多出一个“单词” —— 单独的一个撇号 `'` 所以:如果原始文本中存在 **孤立的单引号 `'`**,就会被保留下来成为一个长度为1的字符串,在 `split()` 后进入列表,从而被计入单词总数。 这就是造成 **多统计单词** 的根本原因! --- ### ✅ 解决方案:在清洗之后,过滤掉仅由 `'` 构成或无意义的项 我们需要在 `replace_txt` 中增加一步:**去除只包含撇号或无实质内容的“单词”** 修改后的代码如下: ```python import re def read_file(filename): with open(filename, 'r') as file: text = file.read() return text def replace_txt(text): # 将除了字母、数字、单引号之外的所有字符替换为空格 cleaned = re.sub(r"[^a-zA-Z0-9']", ' ', text) # 按空白切分 words = cleaned.split() # 过滤掉仅由单引号成的无效单词,如 "'", "''" 等 filtered_words = [word for word in words if not word.strip("'") == ""] return filtered_words if __name__ == '__main__': name = input().strip() txt = read_file('step7/' + name) result = replace_txt(txt) print(len(result)) ``` #### 🔍 关键解释: ```python [word for word in words if not word.strip("'") == ""] ``` - `word.strip("'")`:去掉字符串前后所有的 `'` - 如果结果为空字符串(比如原词是 `"'"` 或 `"'''"`),说明它是纯撇号 → 应剔除 - 否则保留(如 `"I'm"` → strip后是 `"Im"` ≠ "" → 保留) 例如: - `'` → strip后为 `""` → 被过滤 ✅ - `''''` → strip后为 `""` → 被过滤 ✅ - `I'm` → strip后为 `Im` ≠ "" → 保留 ✅ - `don't` → 保留 ✅ - `'hello'` → strip后为 `hello` ≠ "" → 保留 ✅ --- ### 🧪 效果验证 假设原来因孤立 `'` 而多了 8 个无效单词(正好对应差值 1665 - 1657 = 8),那么加上这个过滤条件后,应能准确输出 `1657` --- ### 最终修正版代码(推荐提交版本) ```python import re def read_file(filename): with open(filename, 'r') as file: text = file.read() return text def replace_txt(text): # 保留字母、数字、单引号,其余替换为空格 cleaned = re.sub(r"[^a-zA-Z0-9']", ' ', text) # 切分成单词 words = cleaned.split() # 过滤掉只有单引号成的无效项 return [w for w in words if w.strip("'") != ""] if __name__ == '__main__': name = input().strip() txt = read_file('step7/' + name) result = replace_txt(txt) print(len(result)) ``` --- ### 知识点(列出该代码中遇到的知识点) - **正则表达式否定字符集应用**:使用 `[^a-zA-Z0-9']` 精准匹配需替换的非法字符。 - **字符串 `strip(char)` 方法**:移除首尾指定字符,可用于判断是否为纯撇号单词。 - **列表推导式与条件过滤**:高效剔除无效token,保证统计准确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值