题目大意

把一个长为n(n<=105)的数划成若干段,使得划分出的数最大值和最小值的差尽量的小。(新数不能有前导 0)

解题思路

首先,要使最大差值尽可能小,脑袋一拍想到:如果把这个数每一位都划一段,不是最大值能控制在9以内了?
有了这个性质,再往里思考,因为最大是9,再脑袋一拍,就能想到划分只有两种情况:每段长度相等,或者长度最大差1。

对于每段长度相等的情况,我们只需要枚举长度n的每个因数,分别从前到后求出最大差,取min即可。
对于长度差1的情况,答案若要小于9,只可能是出现了99…9x和100…0y这样的划分,其中x=2...9中的数字, y=0...7中的数字。

注意:如果位数是1和2,那么 99…9x 这个构型起始并不是9开始的,而是单个x,需要特判。
99...9x可能需要拆成若干个,所以应该找更长的100…0y类型。

AC代码

#include<bits/stdc++.h>
using namespace std;
int a[200010],b[200010],c[200010];
char s[200010];
void minu(int *x,int *y,int n)
{
	int i;
	for(i=n;i>=1;i--) x[i]-=y[i];
	for(i=n;i>=1;i--)
		if(x[i]<0) x[i-1]--,x[i]+=10;
}
bool cmp(int *x,int *y,int n)
{
 	for(int i=1;i<=n;i++)
 		if(x[i]!=y[i]) return x[i]<y[i];
 	return 0;
}
int md(int n,int l)
{
	if(!a[1] && l>1) return 9;
	int *m1=a,*m2=a,i;
    for(i=0;i<n;i+=l)
	{
        if(!a[i+1] && l>1) return 9;
        if(cmp(a+i,m1,l)) m1=a+i;
        if(cmp(m2,a+i,l)) m2=a+i;
    }
    memcpy(b,m2,sizeof(int)*(l+10));
    minu(b,m1,l);
    for(i=1;i<l;i++)
		if(b[i]) return 9;
    return b[l];
}
int zz(int n,int l)
{
	int x=1,t0=0,t9=9;
    while(x<=n)
	{
        if(a[x]==1)
		{
            if(x+l>n || c[x+l-1]-c[x]) return 9;
            t0=max(t0,a[x+l]),x+=l+1;
        }
        else
		{
            if(x+l-1>n || c[x+l-2]-c[x-1]!=9*(l-1)) return 9;
	        t9=min(t9,a[x+l-1]),x+=l;
		}
    }
    return t0-t9+10;
}
int main()
{
	int t,n,ans,tot,i;
	scanf("%d",&t);
	while(t--)
	{
		ans=9;
		scanf("%d",&n);
		scanf("%s",s);
		for(i=1;i<=n;i++) a[i]=s[i-1]-'0';
		for(i=1;i<=n;i++)
			c[i]=c[i-1]+a[i];
		for(i=1;i<=n/2;i++)
		{
		    tot=zz(n,i);
		    ans=min(ans,tot);
		    if(n%i==0)
		    {
		    	tot=md(n,i);
		    	ans=min(ans,tot);
			}
		}
		printf("%d\n",ans);
	}
    return 0;
}