HDU 6351 Beautiful Now 全排列,预处理,暴力

题意:十进制数n.操作:可以交换n的任意两个digit的位置. 
T<=100.1<=n,k<=1e9. 问k次操作后,能得到最小和最大的数字?

贪心:假如要大,当前数字和后面最大数字交换.假如有多种 不一定选最后面的..有反例..

n只有9位数(1e9情况只有一种).暴力枚举全排列(第i位变到第p[i]位),
一个排列到另外一个排列最少的交换次数为:sigma(每个循环节的长度-1).
假如有循环节 1->x->y->...m->1.  那么交换一次(1,x) 则变换为 x->x.  1->y->..->m->1. 交换一次长度减少1.

因为T<=100. 预处理出排列变化的代价. O(n!*(n+T)).
 

#include <bits/stdc++.h>
using namespace std;
const int N=15;
int T,k,a[N],vis[N];
char n[N];
vector<int> perm[N][N];
void init(){
	for(int t=1;t<=9;t++){
		for(int i=1;i<=t;i++)	
			a[i]=i;
		memset(vis,0,sizeof(vis));
		do{
			int now=0,tmp=0;
			for(int i=1;i<=t;i++)	
				now=now*10+a[i];
			for(int i=1;i<=t;i++){
				if(vis[i]==now)	continue;
				for(int j=a[i];j!=i;j=a[j])
					vis[j]=now,tmp++;
			}
			perm[t][tmp].push_back(now);
		}while(next_permutation(a+1,a+1+t));
	}
}
int calc(int mask){
	int tmp=1,res=0;
	while(mask){
		res+=a[mask%10]*tmp;
		tmp*=10;
		mask/=10;
	}
	return res;
}
int main(){
	init();
	scanf("%d",&T);
	while(T--){
		scanf("%s%d",n,&k);	
		int t=strlen(n);
		if(t==10){
			printf("%s %s\n",n,n);
			continue;
		}
		if(k>=t)	k=t-1;
		for(int i=1;i<=t;i++)
			a[i]=n[i-1]-'0';
		int mn=1e9,mx=0;
		int lj=1;
		for(int i=1;i<t;i++)
			lj*=10;
		for(int i=0;i<=k;i++){
			for(auto it=perm[t][i].begin();it!=perm[t][i].end();it++){
				int val=calc(*it);
				if(val<lj)	continue;//lead zero
				val<mn?mn=val:0;
				val>mx?mx=val:0;
			}
		}
		printf("%d %d\n",mn,mx);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/noone0/article/details/81478304
今日推荐