HDU 6351 (Beautiful Now) 2018 Multi-University Training Contest 5
题意:给定数N(1<=N<=1e9),k(1<=k<=1e9),求对N的任意两位数交换至多k次能得到的最小与最大的数,每一次交换之后不能出现前导零。
因为N最多只有10位,且给了2500ms,当时觉得可以枚举全排列,再判断前导零和最少交换次数。
最少交换次数是(每个循环节中的个数-1)之和。
当时想的是全排列N的每位数,但是这样会出现一个问题:N中可能出现相同的数,这样求循环节中元素个数就会很困难。‘
其实应该对下标进行全排列,因为下标是不可能相同的,这样就可以O(len) 地计算出每个排列的最少交换次数。然后取符合条件的最小最大的数为答案。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn =10; const LL INF= (1LL)<<60; int k,len; int pos[maxn]; int num[maxn]; bool vis[maxn]; int check(){ memset(vis,0,sizeof(vis)); int cnt =0; for(int i=0;i<len;++i){ if(vis[i]) continue; int tmp=0; while(!vis[i]){ tmp++; vis[i]=1; i = pos[i]; } cnt += tmp-1; if(cnt>k) return 0; } return cnt; } char str[maxn]; int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif int T; scanf("%d",&T); while(T--){ memset(str,0,sizeof(str)); scanf("%s %d",str,&k); len = strlen(str); LL N=0; for(int i=0;i<len;++i) num[i] = str[i]-'0',pos[i]=i,N = N*10+num[i]; LL ans1= N,ans2=N; do{ if(num[pos[0]]!=0 && check()){ LL tmp =0; for(int i=0;i<len;++i){ tmp*=10; tmp+= num[pos[i]]; } if(tmp<ans1) ans1=tmp; if(tmp>ans2) ans2=tmp; } }while(next_permutation(pos,pos+len)); printf("%lld %lld\n",ans1,ans2); } return 0; }
为了更好的明天