UVA - 11552 Fewest Flops

传送门:

题目大意:给你一个字符串,可以平均分成很多段,每一段之内的元素可以任意排序,最后再按原来的顺序把每一段拼起来,问最少的块数。(块:连续相同的一段字符成为一个块)

题解:

  首先我们可以发现,每一个段之内先排好序,然后如果相邻的两端有相同的元素,就可以把这两个元素分别放在尾和首,就可以减少一个块了。于是一个贪心的做法油然而生:每次对于相邻的两段,如果有相同的元素,就可以把他们放在一起,也就是答案-1.但是显然有问题,如果这一个块与上一个,这一个块与下一个是相同的公共的字符,且有且仅有一种公共的字符,就不能减两次,但是如果这一个块只有一种元素,比如:“aaaaa”,那么又可以与两端共用。
  MD贼麻烦啊!
  于是放弃贪心,果断DP即可,因为如果我们知道26个字母每一个的状态,就没有那么多麻烦的特判了。算一下复杂度,O(len*26*26)也是可以的。
  设dp[i][j]代表第i段与第i-1段之间以某一个元素相邻时,j这一个元素可以与后面的匹配的最下块数。也就是以j作为这一段的结尾,由上一段的结尾进行转移。
  统计出每一个块的不同的元素的个数num[i]。以及每一个块是否有j元素has[i][j]。然后初始化dp[0][0~25]。从1开始,每次枚举前一个段结尾的元素,如果这个元素这一个段也有,那么可以放在一起,答案可以+num[i]-1。否则只能+num[i]。
  要注意的是,如果前面结尾的等于当前枚举的,是不行的,因为当前枚举的也是结尾,两个结尾不能碰到一起,除非————只有一个元素。

 1 #include<queue>
 2 #include<cstdio>
 3 #include<vector>
 4 #include<cstring>
 5 #include<iostream>
 6 #include<algorithm>
 7 #define RG register
 8 #define LL long long
 9 #define fre(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout);
10 using namespace std;
11 const int MAXN=1100,INF=0x3f3f3f3f;
12 int Case,top,k,len,ans;
13 int dp[MAXN][26],num[MAXN];
14 bool has[MAXN][26];
15 char s[MAXN];
16 int main()
17 {
18    scanf("%d",&Case);
19    while(Case--)
20       {
21          scanf("%d%s",&k,s);
22          len=strlen(s);
23          memset(num,0,sizeof num);
24          memset(has,0,sizeof has);
25          for(int i=0;i<len;i++)
26             {
27                if(!has[i/k][s[i]-'a']) num[i/k]++;
28                has[i/k][s[i]-'a']=1;
29             }
30          top=(len-1)/k;
31          memset(dp,0x3f3f3f3f,sizeof dp);
32          for(int i=0;i<26;i++) dp[0][i]=num[0];
33          for(int i=1;i<=top;i++)
34             for(int j=0;j<26;j++)
35                if(has[i-1][j])
36                   for(int l=0;l<26;l++)
37                      if(has[i][l])
38                         {
39                            if(has[i][j] && (num[i]==1 || j!=l)) dp[i][l]=min(dp[i][l],dp[i-1][j]+num[i]-1);
40                            else dp[i][l]=min(dp[i][l],dp[i-1][j]+num[i]);
41                         }
42          ans=INF;
43          for(int i=0;i<26;i++) ans=min(ans,dp[top][i]);
45          printf("%d\n",ans);
46       }
47    return 0;
48 }

 

posted @ 2017-10-23 22:27  D_O_Time  阅读(221)  评论(0编辑  收藏  举报