题目大意:给你一个十进制正整数N,你可以重新排列它的各位数字,使其成为11的倍数,并且要最大,而且不能含有前导零。例如:123重排可以得到123、132、213、231、312、321,其中11的倍数只有132和231,且231更大,所以输出231。数据保证有解。
数据范围:
对于10%的数据:N<1e3;
对于30%的数据:N<1e8;
对于40%的数据:N<1e15;
对于100%的数据:N<1e1000;
**已知:**若一个数为11的倍数,那么它的奇数位的和与偶数位的和之差为11的倍数(不懂的可以去百度)。
我们可以用f[i][j]表示i个数中取余11为j的种数。
思路:
大致说下重点,枚举字符串,当i%2==1时,说明这一位是奇数位,我们算出还剩的偶数位的个数,算出剩下偶数位的应该等于的值,将j从9到0枚举,如果还有这种数字,先将这个数字在f数组中的贡献删掉,看看能否有f数组能否满足f【剩下偶数位】【偶数位的值】,如果能满足,这个j就是我们要输出的,如果不能满足,再将这个数的贡献加上,继续向下枚举,直到能满足为止。
偶数同理。
add函数一定要从后向前枚举,del函数从前向后枚举。
代码是来自这个大佬的。不懂的可以去看下。
代码:
#include<bits/stdc++.h>
using namespace std;
int f[1005][20];
int MAX;
int cnt[11];
void add(int x)
{
for(int i=MAX;i>=0;i--)
for(int j=0;j<11;j++)
f[i+1][(j+x)%11]+=f[i][j];
MAX++;cnt[x]++;
}
void del(int x)
{
for(int i=0;i<=MAX;i++)
for(int j=0;j<11;j++)
f[i+1][(j+x)%11]-=f[i][j];
MAX--;cnt[x]--;
}
char s[2005];
int main()
{
if(-1)printf("sda");
f[0][0]=1;
int sum=0;
scanf("%s",s+1);
int len=strlen(s+1);
for(int i=1;i<=len;i++)
{
sum+=s[i]-'0';
sum%=11;
add(s[i]-'0');
}
int r=sum*6%11;//6为2的逆元
int S,S0=r,S1=r;
int tot;
for(int i=1;i<=len;i++)
{
if(i%2==0)tot=(len+1)/2-(i+1)/2,S=S1;
if(i%2==1)tot=(len)/2-(i)/2,S=S0;
int j;//printf("\n%d %d**\n",tot,S);
for(j=9;j>=0;--j)
{
if(cnt[j]==0)continue;
del(j);
if(f[tot][S])break;
add(j);
}
if(i%2==0)S0=(S0-j+11)%11;
if(i%2==1)S1=(S1-j+11)%11;
printf("%d",j);
}
}