题目大意:将给你的数重新排序 要求一个数位原位置与新位置的距离不能超过K,问可以有多少个数是M的倍数(对10007取余),同时求出最小的数。(新数不可以有前导0)
思路:首先考虑 dp[i][j][k] 表示:正在处理第i位,前缀对M取模的余数为j,k是一个二进制数表示周围K个数是否已被用过的数字个数。
我们先从特殊情况入手,我们假设读入为 n=12345678 K=1。
(上图为初始化状态)
其中 k对应的二进制数就是011(2)=3(10)
二进制中1的表示对应数位还没有被占用,同时距离当前数位的距离不超过K,接下来我们考虑转移。
(一) 当前行占用k二进制中的第一个1的位置
则转移为
(二) 当前行占用k二进制中的第二个1的位置
则转移为
从特殊情况中,已经看出了大致的转移方法,尝试放入k二进制中1的位置,再将整体右移处理下一位。不过在本题中还要考虑不能有前导0的情况需要进行判断,同时需要指出的是如果k的二进制中首位是1,则转移只有一种,把首位的1放入,不然之后会出现不够放的情况。
我们在考虑一种特殊情况n=3333 m=1 k=1
答案很明显是1 3333。但是如果只考虑了上述情况的转移就会出现重复的情况答案会是3
3333。接下来考虑如何避免重复。事实上,我们从高位到低位枚举的时候只枚举没有出现过的数字这样就不会重复了。
转移一:
转移二:
因为已经有过对应数位为3的转移,所以这种转移非法要忽视掉。
最后要提醒一点:dp出来的值为0 不一定是0还可能是10007的倍数需要仔细处理。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef int LL;
LL dp[1005][105][130];
pair<int,int> g[1005][105][130];
LL to[1005][105][130];
const LL mod=10007;
char s[1005];
LL a[1005],len,fil,m,k,tem,x,y,newx,newy;
//========================================
LL dfs(LL o,LL mo,LL p){
LL &now=dp[o][mo][p];
if(~now)return now;
if(o==len)return now=(mo==0);
now=0;
if((p>>(2*k))&1){
LL tar=dfs(o+1,(mo*10+a[o-k])%m,fil&(p<<1|(o+k+1<len)));
if(tar){
to[o][mo][p]=a[o-k];
g[o][mo][p]=make_pair((mo*10+a[o-k])%m,fil&(p<<1|(o+k+1<len)));
now=((now+tar+10006)%mod)+1;
}
}else{
bool c[10];memset(c,0,sizeof(c));
for(int i=2*k-1;i>=0;--i){
if((p>>i)&1){
if(c[a[o+k-i]])continue;
c[a[o+k-i]]=1;
if(o==0&&a[o+k-i]==0)continue;
LL tar=dfs(o+1,(mo*10+a[o+k-i])%m,(p^(1<<i))<<1|(o+k+1<len));
if(tar){
if((to[o][mo][p]==-1)||to[o][mo][p]>a[o+k-i]){
to[o][mo][p]=a[o+k-i];
g[o][mo][p]=make_pair((mo*10+a[o+k-i])%m,(p^(1<<i))<<1|(o+k+1<len));
}
now=((now+tar+10006)%mod)+1;
}
}
}
}
return now;
}
//========================================
int main(){
LL i;
while(~scanf("%s",s)){
memset(dp,-1,sizeof(dp));
memset(to,-1,sizeof(to));
len=strlen(s);
for(i=0;i<len;++i)a[i]=s[i]-'0';
scanf("%d%d",&k,&m);
k=min(k,len-1);
if(k==0){
tem=0;
for(i=0;i<len;++i)tem=(tem*10+a[i])%m;
if(tem){
printf("0 -1\n");
}else{
printf("1 ");
for(i=0;i<len;++i)printf("%d",a[i]);
printf("\n");
}
continue;
}
fil=(1<<(2*k+1))-1;
printf("%d ",(dfs(0,0,(1<<(k+1))-1))%mod);
x=0;y=(1<<(k+1))-1;
if(to[0][x][y]==-1){
printf("-1\n");
continue;
}
for(i=0;i<len;++i){
printf("%d",to[i][x][y]);
newx=g[i][x][y].first;
newy=g[i][x][y].second;
x=newx;y=newy;
}
printf("\n");
}
return 0;
}