题意:给定一个字符串,要求用逗号进行分割成连续上升的序列,使得最后一个值最小,如果有多种方案,则使得第一个最大,如果还有则使第二个最大,以此类推。
首先我们可以用贪心的方法,用dp1[i]表示第i位作为当前数字的末位,使得最后一个值最小的方案的位置,那么转移就是i到1枚举j,如果[j,i]的数字大于[dp[j-1],j-1],那么当前[j,i]的数字就是dp[i]的答案了,因为位数更大答案不会更优。
在求出了dp1[n]之后,我们就可以是的最后一个值最小了,假设为[pos,n]构成的数字最小,那我们再用dp2[i]表示第i个数字作为当前数字的第一位,所能构成的最大数字,同样从pos-1开始向前枚举j,如果[i,j]的数字小于[j+1,dp[j+1]]那么当前的数字肯定是最大的,从贪心的角度来看,这样选肯定更优,因为都是i开头,数字越长答案不会更差。在这里要注意,由于写法的原因,直接由dp1[n]的得到的pos是没有去除前导零的,如果有类似0001的数字,会将000与1分开,这样就不正确了,所以要从从dp1[n]的得到的pos一路向前枚举到不是0的位置,将它们的dp2更新为n即可。
下附AC代码
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define maxn 85
using namespace std;
int len;
char s[maxn];
int dp1[maxn],dp2[maxn];
int judge(int l1,int r1,int l2,int r2)
{
while(l1<r1 && s[l1]=='0') l1++;
while(l2<r2 && s[l2]=='0') l2++;
if(r1-l1!=r2-l2) return r2-l2>r1-l1;
if(r1-l1==0) return s[l2]>s[l1];
for(int i=l1;i<=r1;i++)
{
int j=l2+i-l1;
if(s[j]!=s[i]) return s[j]>s[i];
}
return false;
}
int main()
{
while(~scanf("%s",s+1))
{
len=strlen(s+1);
if(len==1 && s[1]=='0') return 0;
dp1[1]=1;
for(int i=2;i<=len;i++)
{
dp1[i]=1;
for(int j=i-1;j>=1;j--)
if(judge(dp1[j],j,j+1,i))
{
dp1[i]=j+1;
break;
}
}
int pos=dp1[len];
dp2[pos]=len;
int i;
for(i=pos-1;i>=1 && s[i]=='0';i--)
dp2[i]=len;
for(;i>=1;i--)
{
dp2[i]=len;
for(int j=pos-1;j>=i;j--)
{
if(judge(i,j,j+1,dp2[j+1]))
{
dp2[i]=j;
break;
}
}
}
int flag=0;
for(int i=1;i<=len;i++)
{
if(!flag) flag=true;
else printf(",");
for(int j=i;j<=dp2[i];j++)
printf("%c",s[j]);
i=dp2[i];
}
printf("\n");
}
}