18杭电多校(hdu 6351) Beautiful Now (置换群 + dfs暴搜+剪枝)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38786088/article/details/81477491

题目链接

一个范围在1~1e9的数字,问做k次两数位互相交换数值(可自己与自己交换),不能有前导0,问最小值和最大值。


先说说自己心里的感受,比赛时想到枚举所有的排列,然后每个排列逐一比较交换次数,选择交换次数不大于k的最大值和最小值。可是突然脑袋卡了,想不起置换群的操作了,突然想起排序后贪心,陷入5h的贪心……,太难理清逻辑了,比赛结束后,又是2h的理清,终于写好!但就是过不了,通过对比数据,发现贪心确实不行,没有办法,删代码,重新写dfs+置换群!也是奇怪,一下就想起来置换群的写法!

/*********
置换群: Next[pos] = i //记录pos位置放原来的i位置上的数
然后找Next[i] i位置放什么,直到找到pos放在哪个位置!就是环!
找出多少个置换群,就是找有多少个环
**********/
int get_group(){
    int ans = 0;
    memset(vis,0,sizeof(vis));
    for(int i=0;i<num;++i){
        if(vis[i]) continue;
        ans++;
        int j = Next[i]; vis[i]  =1;
        while(j!=i) {vis[j]=1;j = Next[j];}
    }
    //cout<<ans<<endl;
    return ans;
}

如果真的直接枚举所有的排序,逐一匹配,会TLE!
我们需要剪枝! 我们排序dfs都是从高位->低位,求最小值时,我们需要数位值从小到大遍历,求最大值时,我们需要数位值从大到小遍历,所以需要分开写!
是不是可以找到一个符合的排列就可以结束了?不行,因为有重复数,
有可能第1次遍历到的9(1)9(3)6(2)5(0) ,需要交换2次;
找下一个9(1)9(3)5(0)6(2) ,需要交换3次;
找下一个9(1)6(2)9(3)5(0) ,需要交换3次;
找下一个9(1)6(2)5(0)9(3) ,需要交换2次;
找下一个9(1)5(0)9(3)6(2) ,需要交换2次;
找下一个9(1)5(0)6(2)9(3),需要交换1次;
如果你停止了,那就找不大9(3)9(1)6(2)5(0) ,交换0和3位置!我在此处wa了3发!


我们换一种方法剪枝!
我们可以想得到,如果我们找到了一个最大值,去找下一个最大值,下一个必须满足前面的数位必须大于或等于此时的答案的数位,(一定要注意:大于的话,后面的数位就可以小于了,这里我wa了3发!),真心无奈!用个变量t记录是否前面枚举的数位大于现在的ans的对应数位!

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <set>
using namespace std;
// 置换群
// a->b
int vis[30];
int flag[30],dight[30];
int Next[30];
const int inf = 1e9+777;
struct node{
    int num,id;
}p[30];

bool cmp(node &a,node &b){return a.num<b.num;}
int num ;
//得到置换群的大小,num-置换群数 就是最小交换次数
int get_group(){
    int ans = 0;
    memset(vis,0,sizeof(vis));
    for(int i=0;i<num;++i){
        if(vis[i]) continue;
        ans++;
        int j = Next[i]; vis[i]  =1;
        while(j!=i) {vis[j]=1;j = Next[j];}
    }
    //cout<<ans<<endl;
    return ans;
}

int Num[30];
//求排序后的合法序列的数值大小
int cala(){
    int ans = 0;
    for(int i=num-1;i>=0;--i){
        ans = ans*10+dight[Next[i]];
    }
   // cout<<ans<<endl;;
    return ans;
}

int ans;
void get_maxn(int x,int k,int t){
    if(x==-1) {
        if(num-get_group()<=k) {
            int tmp = cala();
            if(tmp>ans) {
                ans = tmp;
                for(int i=num-1;i>=0;--i) Num[i] = dight[Next[i]];
            }
        }
        return ;
    }

    for(int i=num-1;i>=0;i--){
        if(t==0&&ans>0&&Num[x]>p[i].num) break;
        if(flag[i]) continue;
        flag[i] = 1; Next[x] = p[i].id;
        get_maxn(x-1,k,t||(ans>0&&Num[x]<p[i].num));
        flag[i] = 0;
    }

    return ;
}
void get_mixn(int x,int k,int t){
    if(x==-1) {
        if(num-get_group()<=k) {
            int tmp = cala();
            if(tmp<ans) {
                ans = tmp;
                for(int i=num-1;i>=0;--i) Num[i] = dight[Next[i]];
            }
        }
        return ;
    }

    for(int i=0;i<num;i++){
        if(t==0&&ans<inf&&Num[x]<p[i].num) break;

        if(flag[i]||(x==num-1&&p[i].num==0))continue;

        flag[i]=1; Next[x] = p[i].id;
        get_mixn(x-1,k,t||(ans<inf&&Num[x]>p[i].num));
        //if(get_mixn(x-1,k)) return true;
        flag[i]=0;
    }
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){

        int n,k;
        scanf("%d%d",&n,&k);
        num = 0;
        while(n){
            dight[num] = n%10;
            p[num].num=  dight[num];
            p[num].id = num;
            num++;
            n/=10;
        }
        sort(p,p+num,cmp);
        memset(flag,0,sizeof(flag));
        ans = inf;
        get_mixn(num-1,k,0);
        printf("%d ",ans);
        memset(flag,0,sizeof(flag));
        ans = -54;
        get_maxn(num-1,k,0);
        //cout<<n<<endl;
        printf("%d\n",ans);

       // cout<<get_group()<<endl;
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38786088/article/details/81477491