http://acm.hdu.edu.cn/showproblem.php?pid=6351
题目
给一个数字n,可以执行k次交换,每次交换都选择数字中的2位,并且交换后不能出现前导0,问能得到的最小值和最大值是多少。
T<=100,n,k<=10^9
题解
方法一
把交换看为对换,枚举排列,然后分解置换,n元循环至少可以分成n-1个对换,而且是最小的分法。
考虑循环$(1quad2quad3quadcdotsquad n)=(1quad2)(1quad3)(1quad4)cdots(1quad n)$
为了拆开循环,每拆一次,原来循环的元素最多减少一个,因此至少n-1个
又发现可以拆成n-1个对换,所以就可以直接判断能不能在k次以内交换实现
分解置换需要$mathcal{O}(n)$
时间复杂度$mathcal{O}(n! imes n)$,需要卡常数,有可能超时
方法二
从前往后依次决定,每次选择最小/最大的数和当前位置交换,并把这个位置指针往后移动一位。时间复杂度$mathcal{O}(n!)$
还可以优化,如果最小/最大的数和当前位置相等,那么直接把这个位置指针往后移动一位。顺序枚举1~1000000000发现跑得很快
AC代码
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; char n[11]; int l; char Ma[11],ma[11]; void dfs1(int k, int p) { if(k>0 && p<l) { char m; for(int i=p; i<l; i++) if(p || n[i]!='0') { m=n[i];} for(int i=p; i<l; i++) if((p || n[i]>'0') && n[i]<m) { m=n[i];} if(m!=n[p]) { for(int i=p; i<l; i++) if(n[i]==m) { swap(n[i], n[p]); dfs1(k-1,p+1); swap(n[i], n[p]); } } else dfs1(k, p+1); } else { if(strcmp(n,ma)<0) { strcpy(ma,n); } } } void dfs2(int k, int p) { if(k>0 && p<l) { char m=n[p]; for(int i=p; i<l; i++) if(n[i]>m) { m=n[i];} if(m!=n[p]) { for(int i=p; i<l; i++) if(n[i]==m) { swap(n[i], n[p]); dfs2(k-1,p+1); swap(n[i], n[p]); } } else dfs2(k, p+1); } else { if(strcmp(n,Ma)>0) { strcpy(Ma,n); } } } int main() { int T; scanf("%d", &T); while(0<T--) { int k; scanf("%s%d", n, &k); strcpy(Ma,n); strcpy(ma,n); l=strlen(n); if(k>l) k=l; dfs1(k,0); dfs2(k,0); printf("%s %s ", ma, Ma); } }