Keyboard Purchase

该博客讨论了如何利用动态规划(DP)中的状态压缩技术解决键盘购买问题。通过计算每个字符出现的次数,计算前缀和,博主展示了如何在O(n * m)的时间复杂度内找到最小成本。博客中还提到了洛古网站上的优化技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Keyboard Purchase
洛古地址
一 看 数 据 范 围 就 知 道 是 状 压 d p , 具 体 地 , d p [ S ] 表 示 已 填 的 集 合 为 S 的 最 小 价 值 , 然 后 我 们 加 一 个 数 的 贡 献 就 是 前 面 的 贡 献 和 对 后 面 的 贡 献 一看数据范围就知道是状压dp,具体地,dp[S]表示已填的集合为S的最小价值,然后我们加一个数的贡献就是前面的贡献和对后面的贡献 dp,dp[S]S
因 为 最 后 是 贡 献 求 和 , 所 以 可 以 对 每 个 点 单 独 算 贡 献 即 ∑ x , y ∈ ( 1 , n ) p o s [ x ] − p o s [ y ] ( p o s [ x ] > p o s [ y ] ) = ∑ i = 1 n s u m 1 i ∗ p o s [ x ] − s u m 2 i ∗ p o s [ x ] 因为最后是贡献求和,所以可以对每个点单独算贡献即\sum_{x,y∈(1,n) pos[x]-pos[y]}(pos[x]>pos[y])=\sum_{i=1}^n sum1_i*pos[x]-sum2_i*pos[x] x,y(1,n)pos[x]pos[y]pos[x]>pos[y]=i=1nsum1ipos[x]sum2ipos[x]
s u m 1 指 i 前 面 的 个 数 , s u m 2 指 i 后 面 的 个 数 sum1指i前面的个数,sum2指i后面的个数 sum1isum2i

P.S 洛古上有一些优化
#include
#include
#include
using namespace std;

const int M=21,N=1e5+5,INF=123456789;
int dp[(1<<M)];
int cnt[25][25],n,m;
char s[N];//这里是N,不是M,调了很久
int main(){
scanf("%d%d",&n,&m);
scanf("%s",(s+1));
int len=strlen(s+1);
for(int i=2;i<=len;i++){
cnt[s[i-1]-‘a’+1][s[i]-‘a’+1]++;
cnt[s[i]-‘a’+1][s[i-1]-‘a’+1]++;
}
for(int i=1;i<=m;i++) cnt[i][i]=0;/不然的话后面计算时会加上cnt[i][i],从而导致答案为负/
memset(dp,0x7f,sizeof(dp));
dp[0]=0;
for(int S=0;S<(1<<m);S++){
int summ=0,T=(S^((1<<m)-1));
bool ex[M];
for(int x=1;x<=m;x++){
if(S&(1<<(x-1))){
ex[x]=1;
summ++;
}
else ex[x]=0;
}
for(int x=1;x<=m;x++){
if(ex[x]) continue;
int tmpsum=0;
for(int y=1;y<=m;y++){
if(!ex[y])
{
tmpsum-=cnt[x][y];
}
if(ex[y]){
tmpsum+=cnt[x][y];
}
}
dp[S|(1<<(x-1))]=min(1lldp[S|(1<<(x-1))],dp[S]+1ll(summ+1)*(tmpsum));
}
}
printf("%d",dp[(1<<m)-1]);
}
检查是要先检查数组范围

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值