uva11552题解(dp)

本文探讨了一种将字符串划分为最小数量的相同字母连续子串的方法,并通过动态规划解决该问题。详细介绍了状态设计、状态转移方程以及边界条件处理,提供了完整的代码实现。

题意是将一字符串划分为若干组,组内可以重排位置,求最后最小包含的块数(即相同字母组成的连续字符串的个数)


这题是序列划分模型,有点类似于 商人那道题,主要是状态的设计,另dp(i,j)为第i个序列首字为j所能划分最小组数,可以很容易得到状态方程 ps额一开始脑洞开得有点大,各种边界没考虑清楚,需要注意的是,假如第i个序列以j开头,并不一定第i-1个序列以j结尾答案就最小,因为我们不能以局部来代替整体,这也是一开始一直wa的原因(泪目)

#include<cstdio>  
#include<cstring>  
#include<cmath>  
#include<cstdlib>  
#include<iostream>  
#include<algorithm>  
#include<vector>  
#include<map>  
#include<queue>  
#include<stack> 
#include<string>
#include<map> 
#include<set>
using namespace std;  
#define LL long long  
const int maxn=1005;
const int INF=1000000000;


int d[maxn][30];
set<int> str[maxn];


int main(){
	int t;
	scanf("%d",&t); 
	while(t--){
		int k;scanf("%d",&k);
		string s;cin>>s;
		
		int len=s.length();
		int grp=len/k;
		for(int i=1;i<=grp;i++) str[i].clear();
		
		for(int i=1;i<=grp;i++){
			for(int j=0;j<k;j++) str[i].insert(s[(i-1)*k+j]-'a');
		}		
		
		for(int i=1;i<=grp;i++){
			int sizei=str[i].size();
			for(set<int>::iterator j=str[i].begin();j!=str[i].end();j++){
				if(i==1){d[i][*j]=sizei;continue;}
				d[i][*j]=INF;
				if(str[i-1].count(*j)){
					if(str[i-1].size()!=1)
						for(set<int>::iterator k=str[i-1].begin();k!=str[i-1].end();k++){
							if(*k!=*j) d[i][*j]=min(d[i][*j],d[i-1][*k]+sizei-1);
							else d[i][*j]=min(d[i][*j],d[i-1][*k]+sizei);
						}
					else d[i][*j]=d[i-1][*j]+sizei-1;
				}
				else{
					for(set<int>::iterator k=str[i-1].begin();k!=str[i-1].end();k++){
						d[i][*j]=min(d[i][*j],d[i-1][*k]+sizei);
					}
				}
			}
		}
		
		int ans=INF;
		for(set<int>::iterator i=str[grp].begin();i!=str[grp].end();i++)
			ans=min(ans,d[grp][*i]);	
		printf("%d\n",ans);

	}
	return 0;
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值