jzoj5230. 队伍统计 (B组——Day9)

本文探讨了一个关于队伍排列的问题,给定n个人和m条矛盾关系,目标是找到所有可能的排列方式,使得不超过k条矛盾关系被违背,最终输出合法排列的数量。

jzoj5230. 队伍统计 (B组——Day9)

题目

Description

现在有n个人要排成一列,编号为1->n 。但由于一些不明原因的关系,人与人之间可能存在一些矛盾关系,具体有m条矛盾关系(u,v),表示编号为u的人想要排在编号为v的人前面。要使得队伍和谐,最多不能违背k条矛盾关系(即不能有超过k条矛盾关系(u,v),满足最后v排在了u前面)。问有多少合法的排列。答案对10^9+7取模。

Input

输入文件名为count.in
第一行包括三个整数n,m,k。
接下来m行,每行两个整数u,v,描述一个矛盾关系(u,v)。
保证不存在两对矛盾关系(u,v),(x,y),使得u=x且v=y 。

Output

输出文件名为count.out。
输出包括一行表示合法的排列数。

Sample Input

输入1:

4 2 1
1 3
4 2

输入2:

10 12 3
2 6
6 10
1 7
4 1
6 1
2 4
7 6
1 4
10 4
10 9
5 9
8 10

Sample Output

输出1:

18

输出2:

123120

Data Constraint

对于30%的数据,n<=10
对于60%的数据,n<=15
对应100%的数据,n,k<=20,m<=n*(n-1),保证矛盾关系不重复。

代码

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

int const mod=1e9+7;
int a[21],f[1<<21][21],t[1<<21];
int n,m,k,ans;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值