20.2.19排位赛C

【题目大意】
有一个长度为n(1<=n<=10^5),均由小写字母组成的字符串st,且最大的字母不超过M(1<=M<=26,将字母用数字表示,a为1).有M*M的矩阵a,a[i][j]表示将i修改j要花费a[i][j]的费用。求将字符串st修改成字符串s,字符串s满足s[i]=s[j],s[i-1]≠s[i]且s[j]≠s[j+1],j-i+1>=k。
【解题思路】
先用floyd算法处理a[i][j],保证a[i][j]是最小的。
g[i][j]表示将st前i个字符全变成j的费用。
(字符串的第一位下标为1)
f[i][j]表示长度为i,第i位为j且满足题意的最小花费。
第i为可以接着上一段,也可以重新开一段(i-k+1 ~ i)
f[i][j]=min(f[i-1][j],min(f[i][k])+g[i][j]-g[i-k][j]) ,1<=k<=M
【代码】

#include <cstdio>
#include <string>
#include <iostream>
using namespace std;
string st;
long long a[30][30];
long long f[101010][30];
long long g[101010][30];
int main()
{
	int n,m,k;
	scanf("%d%d%d",&n,&m,&k);
	cin>>st;
	st='X'+st;
	for (int i=1;i<=m;i++)
	  for (int j=1;j<=m;j++)
	    scanf("%lld",&a[i][j]);
	    
	for (int k=1;k<=m;k++)
	  for (int i=1;i<=m;i++)
	    for (int j=1;j<=m;j++)
		  if (a[i][j]>a[i][k]+a[k][j]) a[i][j]=a[i][k]+a[k][j];
	
	for (int i=1;i<=m;i++)	
	  for (int j=1;j<st.size();j++)     
	    g[j][i]=g[j-1][i]+a[st[j]-'a'+1][i];
	    
	for (int i=1;i<=m;i++)
	  f[k][i]=g[k][i];
		  
	for (int i=k+1;i<st.size();i++)   
	  {
	  	  long long mn=9999999999999;
	  	  if (i-k<k)
	  	  {
	  	  	for (int s=1;s<=m;s++)
	  	  	  f[i][s]=g[i][s];
		  }else
		  {
		  	for (int s=1;s<=m;s++)
		  	  if (f[i-k][s]<mn) mn=f[i-k][s];
		  	for (int s=1;s<=m;s++)
			  f[i][s]=mn+g[i][s]-g[i-k][s];
			for (int s=1;s<=m;s++)
			  if (f[i-1][s]+a[st[i]-'a'+1][s]<f[i][s])
			     f[i][s]=f[i-1][s]+a[st[i]-'a'+1][s];  
		  }
	   }
	 long long ans=9999999999999;  
	for (int i=1;i<=m;i++)
	  if (ans>f[st.size()-1][i]) ans=f[st.size()-1][i];
	printf("%lld\n",ans);  
	    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值