【题解】
经典的1D1D动态规划优化
状态转移方程:f[i]=min{ f[j]+abs(s[i]-s[j]+i-j-1-l)^p }
决策单调性及证明:
http://blog.csdn.net/jasonzhu8/article/details/5928552
因此,对于每个数,其能作为最优决策的区间一定是连续的一段,
对于新求出的f[i],从后往前在i-1,i-2,…对应的决策区间内二分查找 i的决策区间
用一个栈去维护每个数对应的决策区间即可
此题f值巨大,需先用double计算,判断答案是否小于10^18,如果是,在用long long算一遍(double精度不够)
【代码】
#include<stdio.h> #include<stdlib.h> #include<string.h> #define INF 1000000000000000000ll typedef long long LL; char poem[100005][35]; double s[100005],f[100005]; int sta[100005],L[100005],R[100005],id[100005],from[100005]; int n,p,l,ps; int max(int a,int b) { if(a>b) return a; return b; } double jdz(double x) { if(x<0) x=-x; return x; } LL jdz_L(LL x) { if(x<0) x=-x; return x; } double W(int j,int i) { double a=jdz(s[i]-s[j]+i-j-1-(double)l),ans=1; int k; for(k=1;k<=p;k++) ans*=a; return ans; } LL W_L(int j,int i) { LL a=jdz_L((LL)s[i]-(LL)s[j]+(LL)i-(LL)j-1-(LL)l),ans=1; int k; for(k=1;k<=p;k++) ans*=a; return ans; } void jiaru(int k) { int left,mid,right,i; for(;ps>0;ps--) { left=max(L[ps],k+1); if(f[k]+W(k,left)<=f[sta[ps]]+W(sta[ps],left)) { if(L[ps]<=k+1) { if(L[ps]==k+1) ps--; else R[ps]=k; sta[++ps]=k; L[ps]=k+1; R[ps]=n; id[k+1]=k; return; } } else { right=R[ps]; while(left<right)//左闭右开区间 { mid=(left+right+1)/2; if(f[k]+W(k,mid)<=f[sta[ps]]+W(sta[ps],mid)) right=mid-1; else left=mid; } if(left==n) return; R[ps]=left; sta[++ps]=k; L[ps]=left+1; R[ps]=n; id[left+1]=k; return; } } } LL get(int k) { if(k==0) return 0; return get(from[k])+W_L(from[k],k); } void print(int k) { int i; if(k==0) return; print(from[k]); for(i=from[k]+1;i<=k;i++) { printf("%s",poem[i]); if(i<k) printf(" "); } printf("\n"); } int main() { int T,i,j; scanf("%d",&T); for(;T>0;T--) { memset(poem,0,sizeof(poem)); memset(s,0,sizeof(s)); memset(id,0,sizeof(id)); memset(from,0,sizeof(from)); scanf("%d%d%d",&n,&l,&p); for(i=1;i<=n;i++) { scanf("%s",poem[i]); s[i]=(double)strlen(poem[i])+s[i-1]; } sta[ps=1]=0; L[ps]=1; R[ps]=n; j=0; for(i=1;i<=n;i++) { if(j<id[i]) j=id[i]; f[i]=f[j]+W(j,i); from[i]=j; jiaru(i); } if(f[n]>INF) printf("Too hard to arrange\n"); else { printf("%lld\n",get(from[n])+W_L(from[n],n)); print(n); } printf("--------------------\n"); } return 0; }