UVa 11552 - Fewest Flops(DP,状态设计)

该博客探讨了如何通过重新排列字符串中每k个字符组成的块来减少相同字符连续出现的'块'数量,旨在找到重组后具有最少'块'的方案。作者介绍了动态规划的思路,用于计算以特定字符结尾时的最小块数,并给出了解决问题的算法描述。
摘要由CSDN通过智能技术生成

FEWEST FLOPS

A common way to uniquely encode a string is by replacingits consecutive repeating characters (or “chunks”) by the number oftimes the character occurs followed by the character itself. Forexample, the string “aabbbaabaaaa” may be encoded as “2a3b2a1b4a”.(Note for this problem even a single character “b” is replaced by“1b”.)

Suppose we have astring S and anumber k suchthat k divides the lengthof S.Let Sbe the substringof S from 1to kSbethe substringof S from +1 to 2k, and so on. We wish to rearrange the characters ofeachblock Si independently sothat the concatenation of thosepermutations S’ has as few chunks of thesame character as possible. Output the fewest number ofchunks.

For example,let be “uuvuwwuv”and be 4.Then Sis “uuvu” and hasthree chunks, but may be rearranged to “uuuv” which has two chunks.Similarly, Smay berearranged to “vuww”. Then S’,orS1S2, is “uuuvvuww” which is 4 chunks, indeed theminimum number of chunks.

Program Input

The input begins with a linecontaining (1≤ ≤ 100), the number oftest cases. Thefollowing lines containan integer and astring S made of no morethan 1000 lowercase English alphabet letters. It is guaranteedthat k will divide thelength of S.

Program Output

For each test case, output a single line containing theminimum number of chunks after werearrangeS as describedabove.


INPUT

2
5 helloworld
7 thefewestflops


OUTPUT

8

10


题意:输入一个正整数 k 和一个字符串 S, 字符串的长度保证为 k 的整数倍。把 S 的字符串从左至右的顺序每 k 个分成一组,每组里面的字符串可以任意重排,但组与组间的顺序不能改变。你的任务是使重排后的字符串包含的 “块”

尽量的少,每个 “块” 为连续的相同的字母。比如 uuvuwwuv 可以分为两组:uuvu 和 wwuv,第一组重排为 uuuv , 第二组重排为 vuww,连接起来就是 uuuvvuww, 包含4个 “块”。


思路:

dp[i][j]: 第 i 块以第 j 位结尾时的最小块数

对于每个单独的一块,它的chunks就是等于出现的不同字母数

第 i 块的chunks记做 chunks[i]
如果第 i-1 块的最后一位和第 i 块的第一位是一样的,那么可以合并这两个,总chunks可以减少一个


dp[i][j] = min{  如果i-1块的第k位和i块的第一位不同:dp[i-1][k]+chunks[i], 
                       如果i-1块的第k位和i块的第一位相同: dp[i-1][k]+chunks[i]-1  }


如果第 i 块的字母都相同,且第 i 块的首字母在 i-1 块中有出现,则dp[i][j] =dp[i-1][k]


<span style="font-size:18px;">#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;

const int INF = 1<<29;
const double PI = acos(-1.0);
const double e = 2.718281828459;
const double eps = 1e-8;
const int MAXN = 1010;
int vis[256];
char s[MAXN];
int dp[MAXN][MAXN];

int main()
{
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    int Case, kk, chunk;
    cin>>Case;
    while(Case--)
    {
        cin>>kk>>s;
        int len = strlen(s);
        for(int i = 0; i < len/kk; i++)
        {
            fill(dp[i], dp[i]+kk, INF);
            memset(vis, 0, sizeof(vis));
            chunk = 0;
            for(int j = i*kk; j <= (i+1)*kk-1; j++)
            {
                if(!vis[s[j]])
                {
                    vis[s[j]] = 1;
                    chunk++;
                }
            }
            if(!i)
            {
                for(int j = 0; j < kk; j++)
                    dp[i][j] = chunk;
                continue;
            }
            for(int j = 0; j < kk; j++)
            {
                for(int k = 0; k < kk; k++)
                {   // 在第 i-1 块里面选择第 k 位的字母,作为第 i-1 块的末位,同时作为第 i 块的首位
                    int front = (i-1)*kk+k;
                    int rear = i*kk+j;// 在第 i 块里面选择第 j 位的字母,作为第 i 块的末位
                    if(vis[s[front]] && (chunk==1||s[front]!=s[rear])) 
                        //第 i 块的首位和末位不可能为同个字母,除非第 i 块的所有字母都相同
                        dp[i][j] = min(dp[i][j], dp[i-1][k]+chunk-1);
                    else
                        dp[i][j] = min(dp[i][j], dp[i-1][k]+chunk);
                }
            }
        }
        int ans = INF;
        for(int i = 0; i < kk; i++)
            ans = min(ans, dp[len/kk-1][i]);
        cout<<ans<<endl;
    }
    return 0;
}
</span>


本文参考:http://blog.csdn.net/shuangde800/article/details/9856943#comments

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值