UVA 12594 Naming Babies 斜率DP

Naming Babies UVA - 12594

给一个序列 P N PN PN,该序列是 a − z a-z az 的排列,再给一个序列 N M = C 0 C 1 ⋯ C n − 1 ‾ NM=\overline{C_0C_1\cdots C_{n-1}} NM=C0C1Cn1,对于该序列的第 i i i 个字符 C i C_i Ci,定义 P O S i POS_i POSi 为字符 C i C_i Ci 在序列 P N PN PN 中的位置(从 0 0 0 开始),定义总体花费为:

∑ i = 0 n − 1 ( i − P O S i ) ∗ P O S i \sum_{i=0}^{n-1}(i-POS_i)*POS_i i=0n1(iPOSi)POSi

题目给了个例子:

在这里插入图片描述
然后把这个序列分成 k k k 段,然后每段分别计算这个花费,之后再求和,问最小花费和是多少?

d p [ i ] [ j ] dp[i][j] dp[i][j] 为前 i i i 个分 j j j 段,则有递推方程:

d p [ i ] [ j ] = min ⁡ { d p [ k ] [ j − 1 ] + ∑ m = k + 1 i ( m − k − 1 − p [ a [ m ] ] ) ∗ p [ a [ m ] ] } d p [ i ] [ j ] = min ⁡ { d p [ k ] [ j − 1 ] + s [ i ] − s [ k ] − ( k + 1 ) ( s p [ i ] − s p [ k ] ) } \begin{aligned} dp[i][j]&=\min\{dp[k][j-1]+\sum_{m=k+1}^{i}(m-k-1-p[a[m]])*p[a[m]]\}\\ dp[i][j]&=\min\{dp[k][j-1]+s[i]-s[k]-(k+1)(sp[i]-sp[k])\} \end{aligned} dp[i][j]dp[i][j]=min{dp[k][j1]+m=k+1i(mk1p[a[m]])p[a[m]]}=min{dp[k][j1]+s[i]s[k](k+1)(sp[i]sp[k])}

其中 s [ i ] = ∑ m = 0 i ( m − p [ a [ m ] ] ) p [ a [ m ] ] , s p [ i ] = ∑ m = 0 i p [ a [ m ] ] s[i]=\sum_{m=0}^i(m-p[a[m]])p[a[m]],sp[i]=\sum_{m=0}^ip[a[m]] s[i]=m=0i(mp[a[m]])p[a[m]],sp[i]=m=0ip[a[m]] a = N M , p [ C i ] = P O S i a=NM,p[C_i]=POS_i a=NM,p[Ci]=POSi ,设 k 1 < k 2 k_1<k_2 k1<k2,且 k 2 k_2 k2 优于 k 1 k_1 k1,则:

d p [ k 2 ] [ j − 1 ] + s [ i ] − s [ k 2 ] − ( k 2 + 1 ) ( s p [ i ] − s p [ k 2 ] ) < d p [ k 1 ] [ j − 1 ] + s [ i ] − s [ k 1 ] − ( k 1 + 1 ) ( s p [ i ] − s p [ k 1 ] ) dp[k_2][j-1]+s[i]-s[k_2]-(k_2+1)(sp[i]-sp[k_2])<dp[k_1][j-1]+s[i]-s[k_1]-(k_1+1)(sp[i]-sp[k_1]) dp[k2][j1]+s[i]s[k2](k2+1)(sp[i]sp[k2])<dp[k1][j1]+s[i]s[k1](k1+1)(sp[i]sp[k1])

d p [ k 2 ] [ j − 1 ] − s [ k 2 ] + s p [ k 2 ] ( k 2 + 1 ) − ( d p [ k 1 ] [ j − 1 ] − s [ k 1 ] + s p [ k 1 ] ( k 1 + 1 ) ) < s p [ i ] ( k 2 − k 1 ) y k 2 − y k 1 x k 2 − x k 1 < s p [ i ] \begin{aligned} dp[k_2][j-1]-s[k_2]+sp[k_2](k_2+1)-(dp[k_1][j-1]-s[k_1]+sp[k_1](k_1+1))&<sp[i](k_2-k_1)\\ \frac{y_{k_2}-y_{k_1}}{x_{k_2}-x_{k_1}}&<sp[i] \end{aligned} dp[k2][j1]s[k2]+sp[k2](k2+1)(dp[k1][j1]s[k1]+sp[k1](k1+1))xk2xk1yk2yk1<sp[i](k2k1)<sp[i]

其中 y k = d p [ k ] [ j − 1 ] − s [ k ] + s p [ k ] ( k + 1 ) , x k = k y_k=dp[k][j-1]-s[k]+sp[k](k+1),x_k=k yk=dp[k][j1]s[k]+sp[k](k+1),xk=k

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
//#define WINE
#define MAXN 20010
using namespace std;
typedef long long ll;
int T,iCase,k,n,p[300],h,t,q[MAXN];
ll s[MAXN],sp[MAXN],dp[MAXN][505];
char PN[30],a[MAXN];
ll up(int k2,int k1,int j){
    return dp[k2][j-1]-s[k2]+(k2+1)*sp[k2]-(dp[k1][j-1]-s[k1]+(k1+1)*sp[k1]);
}
ll down(int k2,int k1){
    return k2-k1;
}
ll getDP(int k,int i,int j){
    return dp[k][j-1]+s[i]-s[k]-(k+1)*(sp[i]-sp[k]);
}
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    scanf("%d",&T);
    while(T--){
        scanf(" %s %d",PN,&k);
        scanf(" %s ",a);
        int len=strlen(PN);
        for(int i=0;i<len;i++)p[PN[i]]=i;
        n=strlen(a);
        for(int i=0;i<n;i++){
            s[i]=(i==0)?0:s[i-1];
            s[i]+=(i-p[a[i]])*p[a[i]];
            sp[i]=(i==0)?0:sp[i-1];
            sp[i]+=p[a[i]];
            dp[i][1]=s[i];
        }
        for(int j=2;j<=k;j++){
            h=t=0;q[t++]=j-2;
            for(int i=j-1;i<n;i++){
                while(h+1<t&&up(q[h+1],q[h],j)<sp[i]*down(q[h+1],q[h]))
                    h++;
                dp[i][j]=getDP(q[h],i,j);
                while(h+1<t&&up(i,q[t-1],j)*down(q[t-1],q[t-2])<=up(q[t-1],q[t-2],j)*down(i,q[t-1]))
                    t--;
                q[t++]=i;
            }
        }
        printf("Case %d: %lld\n",++iCase,dp[n-1][k]);
    }
    return 0;
}

在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值