分段动态规划(SOJ 1162)
问题:给出一个长度为$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)技术来处理。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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;
}
相似问题:SOJ 2113: 数字游戏