最长上升子序列类动态规划
dp[i] = min(dp[i],dp[j] + k)
题意:给定若干单词,把它们排成每行字符数为n的若干行,词与词之间可填充任意个空格,设某个单词与单词之间的空格数为g,(g-1)^2称为坏点,
求使所有坏点和最小得排版方式.
技巧:因为要构造字母序最小的最优解,所以可以从后往前进行DP计算
View Code
#include<cstdio> #include<cstring> #define MAXN 10010 using namespace std; char word[MAXN][82]; int a[MAXN],aft[MAXN],b[MAXN],m,n; void Blank(int k) { for(;k--;) putchar(' '); } int Cal(int i, int j) { int g,t,k,ret; if(j==i+1) return 500; t=n-(a[i]-a[j]); k=j-i-1; g=t/k; ret=k*g*g+(t%k)*(2*g+1); return ret; } void Input() { int i,j,k; char s[100]; gets(s); for(k=0;gets(s),s[0]!='\0';) for(i=0;s[i]!='\0'&&s[i]!='\n';) { while(s[i]==' ') i++; if(s[i]=='\0'||s[i]=='\n') break; for(j=0;s[i]!='\0'&&s[i]!='\n'&&s[i]!=' ';j++,i++) word[k][j]=s[i]; word[k++][j]='\0'; } m=k; n++; a[m]=0; for(i=m-1;i>=0;i--) a[i]=(int)strlen(word[i])+1+a[i+1]; } void Solve() { int i,j,k; b[m]=0; for(i=m-1;i>=0;i--) { b[i]=b[i+1]+500; aft[i]=i+1; for(j=i+2;j<=m&&a[i]-a[j]<=n;j++) { k=Cal(i,j); if(b[i]>b[j]+k||(b[i]==b[j]+k&&Cal(i,j)<Cal(i,aft[i]))) b[i]=b[j]+k,aft[i]=j; } } } void Print() { int k,t,g,d,i,j; for(i=0;i<m;i=j) { j=aft[i]; if(j==i+1) { printf("%s",word[i]); putchar('\n'); } else { t=n-(a[i]-a[j]); k=j-i-1; g=t/k; d=k-(t%k); for(;d--;i++) { printf("%s ",word[i]); Blank(g); } for(;i+1<j;i++) { printf("%s ",word[i]); Blank(g); } puts(word[i]); } } } int main() { for(;scanf("%d",&n),n;) { Input(); Solve(); Print(); putchar('\n'); } return 0; }