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
",ans1,ans2);
    }
    return 0;
}
为了更好的明天
原文地址:https://www.cnblogs.com/xiuwenli/p/9432817.html