分段动态规划(SOJ 1162)

SOJ 1162: I-Keyboard 

问题:给出一个长度为$L$的字符串$S$,其中每个字符有个频数$F[i], 0\le i<L$.现在把$S$分成$K$段满足积和(sum of products)最小。对于每个段,其中的每个字符有个位置$P[i]$值从$1$到这个段的长度。

字符串:         $S[0], \dots, S[i_{1}]$ | $S[i_{1}+1], \dots, S[i_{2}]$ | $\dots$ | $S[i_{K-1}+1], \dots, S[i_{K}]$ $(S[L-1])$

段:                $1$    |       $2$      | $\dots$ |       $K$ 

位置:          $1, \dots, i_{1}+1$ $1, \dots, i_{2}-i_{1}$   | $\dots$ |  $1, \dots, i_{K}-i_{K-1}$

频数:        $F[0], \dots, F[i_{1}]$ | $F[i_{1}+1], \dots, F[i_{2}]$| $\dots$ | $F[i_{K-1}+1], \dots, F[i_{K}]$ $(F[L-1])$

积和cost的定义如下:

$cost=\Sigma P[i]*F[i].$

分析:这个问题是一个动态规划问题。最优子结构为: 如果$cost(K, i_{K})$是字符串$S[0, \dots, i_{K}]$分成$K$个段的最优解,则$cost(K-1, i_{K-1})$是字符串$S[0, \dots, i_{K-1}]$分成$K-1$个段的最优解。定义$cost[i][j]$为字符串$S[0, \dots, j]$分成$i$个段的最优解,则状态转移方程为:

$cost[i][j]=\min\{cost[i-1][k]+\sum_{t=k+1}^{j}(t-k)*F[t], k=i-1, \dots, j-1\}$.

即把每个字符串$S[k+1, \dots, j], k=i-1, \dots, j-1$作为第$i$个段。这里需要注意$\sum_{t=k+1}^{j}(t-k)*F[t]$的计算。注意到

$\sum_{t=k+1}^{j}(t-k)*F[t]=\sum_{t=k+1}^{j}t*F[t]-k\sum_{t=k+1}^{j}F[t]$,

因此我们可以用累积和(cumulative sum)技术来处理。

代码:

#include<iostream>
using namespace std;
char key[95];
char letter[95];
int weight[95];
int cost[95][95];
int flag[95][95];
int position[95];
int sum1[95];
int sum2[95];
int main()
{
    int T;
    scanf("%d",&T);
    int K,L;
    int i,j,k,t;
    int tarVal;
    int temp;
    for(t=1;t<=T;t++)
    {
        scanf("%d%d",&K,&L);
        scanf("%s",key);
        scanf("%s",letter);
        scanf("%d",&weight[0]);
        sum1[0]=weight[0];
        sum2[0]=weight[0];
        for(j=1;j<L;j++)
        {
            scanf("%d",&weight[j]);
            sum1[j]=sum1[j-1]+weight[j]*(j+1);
            sum2[j]=sum2[j-1]+weight[j];
        }
        cost[0][0]=weight[0];
        for(j=1;j<L;j++)
            cost[0][j]=sum1[j];
        for(i=1;i<K;i++)
            for(j=i;j<L;j++)
            {
                tarVal=0x3f3f3f3f;   
                for(k=i-1;k<=j-1;k++)
                {
                    temp=cost[i-1][k]+sum1[j]-sum1[k]-(k+1)*(sum2[j]-sum2[k]);
                    if(temp<tarVal)
                    {
                        tarVal=temp;
                        flag[i][j]=k+1;
                    }
                }
                cost[i][j]=tarVal;
            }
        temp=L-1;
        for(i=K-1;i>0;i--)
        {
            position[i]=flag[i][temp];
            temp=position[i]-1;
        }
        position[0]=0;
        position[K]=L;
        printf("Keypad #%d:\n",t);
        for(i=0;i<K;i++)
        {
            printf("%c: ",key[i]);
            for(j=position[i];j<position[i+1];j++)
                printf("%c",letter[j]);
            printf("\n");
        }
        printf("\n");
    }
    return 0;
}
View Code

相似问题:SOJ 2113: 数字游戏

posted on 2019-03-11 03:06  小叶子曰  阅读(361)  评论(0编辑  收藏  举报

导航