UVA 12594 Naming Babies 斜率DP
给一个序列 ,该序列是 的排列,再给一个序列 ,对于该序列的第 个字符 ,定义 为字符 在序列 中的位置(从 开始),定义总体花费为:
题目给了个例子:
然后把这个序列分成 段,然后每段分别计算这个花费,之后再求和,问最小花费和是多少?
设 为前 个分 段,则有递推方程:
其中 , ,设 ,且 优于 ,则:
其中 。
代码如下:
#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;
}