1 条题解
-
0
自动搬运
来自洛谷,原作者为

pocafup
路 是人走出来的搬运于
2025-08-24 22:28:30,当前版本为作者最后更新于2021-01-31 07:28:39,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
Uddered but not Herd 题解
之前交错题解区了神仙状压dp,赛时联想到状压的时候简直惊了。以后自己出题也会往这个方面想了
发现如果某个字母没有在他听到的东西内出现,那么这个字母毫无意义。同理,如果一个字母出现过,那么他必然有意义。
于是,题目转化为求一个子串,每次让这个子串跟给出的字符串进行匹配,求最小的匹配次数。
没思路?看一眼数据范围,发现刚刚好有6个字母不需要用。一共个 26 字母,6个不用,算下来只用计算20个字母。符合范围的好像只有状压了。
设当前的子串为 , 考虑在子串后面加入字母算贡献。发现每个字母都是独立的。换言之,不论之前子串的顺序如何,这个字母跟之前子串中的字母产生的贡献都相等。如果在字符串中当前字母前一个位置的字母在子串出现过,那么加入这个字母一定会使最终朗诵次数减少一次。
枚举二元组暴力预处理出当子串存在字母 ,目前要加入字母 时的贡献,然后状压一下,每次加字母时积累贡献即可。字母要离散一下。答案即为 - 贡献
赛时写的方法是 的,但应该可以通过一些状压上的小优化做到
代码只放主函数,需要全部代码的私戳
signed main(){ cin >> ch; int n = ch.size(); For(i,0,ch.size()-1) { pos[i+1] = (int)(ch[i]-'a')+1; mp[pos[i+1]] = true; } For(i,1,26) if (mp[i]) head[i] = ++tot; For(i,1,n) pos[i] = head[pos[i]];//简单的离散,懒人直接用 map 瞎弄了 //pref i j : 在字母 i 前一位的字母 j 数量 For(i,1,tot){ For(j,1,tot){ int v = 0,tmp = 0; For(k,1,n)if (pos[k]==i && pos[k-1]==j) tmp++; pref[i][j]= tmp; } } For(i,1,1<<tot){ For(j,1,tot){ if ((i>>(j-1)) & 1) continue; int tmp = 0; For(k,1,tot) if ((i>>(k-1))&1) tmp += pref[j][k];//将所有贡献累计 dp[i+(1<<(j-1))] = chkmax(dp[i+(1<<(j-1))],dp[i]+tmp);//转移 ans = chkmax(ans,dp[i+(1<<(j-1))]);//人懒,不想去想最终状态,直接取max了 } } cout << n-ans; }
- 1
信息
- ID
- 6444
- 时间
- 2000ms
- 内存
- 250MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者