重排题

题目大意:给你一个十进制正整数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);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值