• UVA


    传送门:

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

    题解:

      首先我们可以发现,每一个段之内先排好序,然后如果相邻的两端有相同的元素,就可以把这两个元素分别放在尾和首,就可以减少一个块了。于是一个贪心的做法油然而生:每次对于相邻的两段,如果有相同的元素,就可以把他们放在一起,也就是答案-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
    ",ans);
    46       }
    47    return 0;
    48 }
  • 相关阅读:
    Openjudge-2694-逆波兰表达式
    POJ-3984-迷宫问题
    HUST软件与微电子学院第八届程序设计竞赛-小乐乐下象棋
    LiberOJ-#6000. 「网络流 24 题」搭配飞行员 (二分图匹配)
    BZOJ-3172: [Tjoi2013]单词 (AC自动姬 fail树)
    BZOJ-1036: [ZJOI2008]树的统计Count(树链剖分+线段树)
    HDU-3966 Aragorn's Story(树链剖分+线段树)
    hihoCoder-1036 Trie图(AC自动姬)
    WHYZOJ-#14 数列(矩阵快速幂)
    WHYZOJ-#66 穿越七色虹(二分)
  • 原文地址:https://www.cnblogs.com/D-O-Time/p/7719566.html
Copyright © 2020-2023  润新知