题目链接:信息学奥赛比赛系统 | 小怪兽吃糖果 (qduoj.com)
题意就是说让我们选择选择吃糖果先后顺序,使得我们的总操作次数最少,看了一下数据范围,糖果的种类小于等于20,显然是要用状压来解决的,我们可以用状态压缩来枚举吃糖果的顺序,然后用dp来更新吃糖果的所需操作。
比如我们有3种糖果,000表示三种糖果都没有被吃掉,010代表第二种糖果被吃掉了,我们每次更新都要选择一种尚未被吃掉的糖果进行更新,就比如对于010这种状态下我们可以选择吃第一种糖果或者第三种糖果,所以我们就需要预处理出来在第二种糖果被吃的情况下吃第一种糖果和第三种糖果分别所需的操作数,最后直接用 dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+cnt[j]) 进行更新就行,其他就没什么需要特别注意的了,代码里面有注释:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
int dp[1<<21];//dp[s]表示状态为s时的最小操作次数
int cnt[25];//cnt[i]表示第i种糖果分布在几段不同的区间内
char s[500];
int a[500];
int main()
{
int n,K;
cin>>n>>K;
cin>>s;
map<char,int>mp;
memset(dp,0x3f,sizeof dp);
dp[0]=0;
int count=0;
for(int i=0;i<n;i++)//将不同种类的糖果映射到连续的自然数上
{
if(mp[s[i]])//如果这种糖果出现过就直接将其标记为他的种类
{
a[i]=mp[s[i]]-1;
continue;
}
mp[s[i]]=++count;//如果还没有出现过就加入map
a[i]=mp[s[i]]-1;//状压一般从0开始
}
for(int i=0;i<1<<K;i++)
{
int pre=-1;
memset(cnt,0,sizeof cnt);//在不同状态下吃同一种糖果所需的操作数是不同的
for(int j=0;j<n;j++)
{
if(i>>a[j]&1) continue;//如果第j个糖果所属的种类已经被吃掉,则继续下一步操作
if(pre==a[j]) continue;//说明当前区间连续
else//发现区间间断点,进行更新
{
cnt[a[j]]++;
pre=a[j];
}
}
for(int j=0;j<K;j++)
if((i>>j&1)==0)//说明第j种糖果尚未被吃
dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+cnt[j]);
}
printf("%d\n",dp[(1<<K)-1]);
return 0;
}