Day 5 B - Beautiful Now HDU 6351 | 暴力 | dfs | 搜索 | 剪枝(TLE之谜)

很睿智的题解

补题的时候看题解,next_permutation枚举我理解,但是看到solve()内部判断当前下标是不是等于i的时候实在懵逼,查了好几篇终于明白了。题解的暴力思路是这样的,限定了交换次数,之后问我们在这个次数下的最大值,最小值。通过next_permutation找出所有可能的排列情况,跟原本的序列号对比(就是i到length的自然序),如果出现不相同,说明出现了一次swap,再向后找当前自然序位置在permutation数组里的位置,两者交换,算一次swapcnt。这样扫下去,如果扫的过程中,swapcnt>k,说明这种排列违法,直接return,进行下一种排列。因为这种情况寻求的是全部合法情况,所以max,min一定包含其中,在函数末尾搞一个min(),max(),全局维护max,min即可,不需要分最大最小分别讨论。也正因此只要判断第一位的情况就可以了,不管是min max第一位都不可以为0,直接return向后进行。

不过这样之后还是T,试了一下直接贴题解的码上去也T了orz实在是非常迷了。
最后发现同样是下面的码,如果是1-len操作就会T,0-len-1操作就可以AC,我也是很???了orz

#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>

using namespace std;

int mino[10],
    numcnt[10], numcnt2[10],//0-9
    permu[10], permucopy[10];

int main()
{
    int T = 0;
    scanf("%d", &T);//读入测试样例的数目
    while(T--)
    {
        //memset(o, '\0', sizeof(o));
        //memset(mino, 0, sizeof(mino));
        memset(numcnt, 0, sizeof(numcnt));
        memset(numcnt2, 0, sizeof(numcnt2));
        //memset(permucopy, 0, sizeof(permucopy));

        int n, k;
        scanf("%d %d", &n, &k);

        int len = 0;
        while(n)
        {
            permu[len++] = n%10;
            //printf("permu[%d] = %d\n", len, permu[len]);
            numcnt[n%10]++;
            numcnt2[n%10]++;
            n /= 10;
        }

        //printf("len = %d\n", len);
        for(int i = len - 1;i >= 0;i--)
        {
            mino[len - i -1] = permu[i];
            //printf("permu[%d] = %d  mino[%d] = %d\n", i+1, permu[i+1], len-i, mino[len-i]);
        }

        for(int i = 0;i < len;i++)
            permu[i] = i;

        int minresult = 0x3f3f3f3f, maxresult = -1;

        if(k >= len-1)
        {
            //输出最小值
            for(int i = 1;i <= 9;i++)
            {
                if(numcnt[i])
                {
                    printf("%d", i);
                    numcnt[i]--;
                    break;
                }
            }

            for(int i = 0;i <= 9;i++)
            {
                while(numcnt[i])
                {
                    printf("%d", i);
                    numcnt[i]--;
                }
            }
            printf(" ");

            //输出最大值
            for(int i = 9;i >= 0;i--)
            {
                while(numcnt2[i])
                {
                    printf("%d", i);
                    numcnt2[i]--;
                }
            }

            printf("\n");
            continue;
        }

        do
        {
            int sum = 0, swapcnt = 0;

            if(mino[permu[0]] == 0)
                continue;

            for(int i = 0;i < len;i++)
                permucopy[i] = permu[i];

            for(int i = 0;i < len;i++)
            {
                if(permucopy[i] != i)//发生过交换
                {
                    swapcnt++;
                    if(swapcnt > k)
                        break;
                    for(int j = i+1;j < len;j++)
                    {
                        if(permucopy[j] == i)
                        {
                            swap(permucopy[i], permucopy[j]);
                            break;
                        }
                    }
                }

                if(swapcnt > k)
                    break;
                sum = sum*10 + mino[permu[i]];
            }

            if(swapcnt <= k)
            {
                maxresult = max(maxresult, sum);
                minresult = min(minresult, sum);
            }
        }while(next_permutation(permu, permu+len));

        printf("%d %d\n", minresult, maxresult);
    }
    return 0;
}


TLE的本人

给你一串数字,和k上限的交换次数,问在k内最大可生成的值和最小可生成的值分别是多少。我的想法是,不论求最大最小,都是第x大/小的数在第x位置上,如果原本的数符合条件,那么跳过,不占用交换次数,循环到下一轮。所以我用struct来储存pos val的信息,写一个cmp来满足val升序排列,pos降序排列。之后依次取用。
对于0的特殊情况,设置一个used属性,如果使用过就标记为1,对于有0的情况,临时变量跳到第一个不是0的值,然后标记为1,但是全局操纵digit下标的值保持不变,每次进行swap的时候先检验一下used的值,如果为1就往后找。
但是可能是因为这样的花销太大,加上为了确认交换之后两边的pos值都要改变,需要一个getit函数取找在digit里的位置,所以一直T。目前就是这样,但是找不到更好的优化方式了。

#pragma GCC optimize(2)
#include <bits/stdc++.h>

using namespace std;

#define endl '\n'
#define ll long long

int T, n, k, size_;
char integer[10], integermax[10];

typedef struct nod
{
    int pos,   //位置
        val;   //值
    bool used = 0,
         zeroflag = 0;
}sw;
sw digit[10];
sw digit2[10];
bool cmp(sw x, sw y)
{
    if(x.val == y.val)
        return x.pos > y.pos;//降序排列
    return x.val < y.val;//升序排列
}

int findit(int val, int pos)
{
    //printf("findit into val = %d pos = %d\n", val, pos);
    int l = 0, r = size_-1, mid = 0;
    //printf("l = %d  r = %d  mid = %d\n", l, r, mid);


    /*printf("\n startgetit\n");
    for(int i = 0;i < size_;i++)
    {
        printf("digit[%d].val = %d\n", i, digit[i].val);
    }*/

    while(l <= r)
    {
        //printf("into while\n");

        mid = l+(r-l)/2;

        //printf("l = %d  r = %d  mid = %d\n", l, r, mid);
        //printf("digit[%d].val = %d  .pos = %d\n", mid, digit[mid].val, digit[mid].pos);
        if(digit[mid].val < val)
        {
            l = mid+1;
        }
        else if(digit[mid].val > val)
        {
            r = mid-1;
        }
        else
        {
            //printf("l = %d  r = %d\n", l, r);
            for(int i = l;i <= r;i++)
                if(digit[i].pos == pos)
                {
                    //printf("mid = %d\n", i);
                    return i;
                }

        }
    }
}

int main()
{
    //printf("start\n");

    scanf("%d", &T);
    while(T--)
    {
        //printf("\n\n");
        memset(digit, 0, sizeof(digit));
        memset(digit2, 0, sizeof(digit2));
        memset(integer, '\0', sizeof(integer));
        memset(integermax, '\0', sizeof(integermax));

        scanf("%s %d", integer, &k);
        for(int i = 0;i < strlen(integer);i++)
        {
            digit[i].pos = i;
            digit[i].val = integer[i] - '0';

            digit2[i].pos = i;
            digit2[i].val = integer[i] - '0';
            //copydigit[i].pos = i;
            //copydigit[i].val = integer[i] - '0';

            integermax[i] = integer[i];
        }

        //排序 从小排到大 更换k个
        sort(digit, digit + strlen(integer), cmp);

        /*for(int i = 0;i < strlen(integer);i++)
        {
            printf("%d %d\n", digit[i].val, digit[i].pos);
        }*/


        size_ = strlen(integer);
        //printf("size = %d\n\n", size_);

        if(k >= size_-1)
        {
            int tr = 0;
            while(digit[tr].val==0)
                tr++;
            digit[tr].used = 1;
            printf("%d", digit[tr].val);
            for(int i = 0;i < size_;i++)
            {
                if(digit[i].used==0)
                    printf("%d",digit[i].val);
            }
            printf(" ");


            for(int i = size_-1;i >= 0;i--)
                printf("%d",digit[i].val);
            printf("\n");
            continue;
        }

        //找最大
        int i = 0, cnt = 0;
        while(cnt < k && i < size_)//k统计当前swap的次数,i表示当前的位置
        {
            //printf("integermax - %s\n", integermax);
            if(digit[size_-1-i].val > integermax[i] - '0')//如果当前位置小于digit对应位置元素
            {
                //printf("integermax[%d] = %c\n", i, integermax[i]);
                //printf("digit[%d] integermax[%d] = %c\n", size_-1-i, digit[size_-1-i].pos, integermax[digit[size_-1-i].pos]);
                integermax[digit[size_-1-i].pos] = integermax[i];//当前元素值赋给digit位置处的元素



                /**如何维护被替换元素的pos值 注意要在integermax[i]改变前**/
                //printf("\nfindit = %d\n", findit((integermax[i]-'0'), i));
                digit[findit((integermax[i]-'0'), i)].pos = digit[size_-1-i].pos;
                //printf("digit[%d].pos = %d\n", findit((integermax[i] - '0'), i), digit[findit(integermax[i], i)].pos);
                integermax[i] = digit[size_-1-i].val + '0';//digit的值赋给当前的元素

                //printf("integermax - %s\n", integermax);

                digit[size_-1-i].pos = i;//当前替换元素的pos改变

                i++;
                cnt++;
                continue;
            }
            i++;
        }




        //找最小
        int flag = 0, //记录跳0操作
            digiti = 0;//记录digiti内部进行到了第几位
            i = 0, //记录n移动到了第几位
            cnt = 0;//记录操作了几次
        while(cnt < k && i < size_ && digiti < size_)
        {
             /*printf("\n\ni = %d  cnt = %d digiti = %d\n", i , cnt, digiti);
            printf("integer - %s\n", integer);
            printf("startwhile\n");
            for(int i = 0;i < size_;i++)
            {
                printf("digit[%d].used = %d .val = %d .pos = %d\n", i, digit[i].used, digit[i].val, digit[i].pos);
            }
           */

            if(i == 0)//如果是数字n的第一位
            {
                if(digit2[i].val == 0)//数字n的第一位为0 需要进行跳零操作
                {
                    //printf("i == 0 digit[i].val == 0\n");

                    int tim = 0;//digit从小到大排,如果有0一定在前面
                    while(digit2[tim].val == 0)
                    {
                        tim++;
                    }

                    digit2[tim].zeroflag = 1;//记录第一个不为0的digit的位置,并标记已经使用
                    digit2[tim].used = 1;
                    integer[digit2[tim].pos] = integer[i];//将i位置的值赋给tim位置的值


                    digit2[findit((integer[i]-'0'), i)].pos = digit2[tim].pos;//被替换元素pos值更新
                    integer[i] = digit2[tim].val + '0';//digittim位置的值赋给当前的元素
                    digit2[tim].pos = i;//当前替换元素的pos改变


                    flag = 1;//记录出现了跳0操作
                    cnt++;//进行了一次swap 交换次数++
                    i++;//数字n向后移位
                    continue;
                }

                else if(digit2[i].val < integer[i] - '0')//第一位不是0,但是digit内部有小于in[i]的值
                {

                    //printf("i == 0 digit[i].val < integer[i] - '0'\n");

                    integer[digit2[i].pos] = integer[i];//将此时第1位的值赋给digit第一个小于i的位置值

                    digit2[findit((integer[i]-'0'), i)].pos = digit2[i].pos;//将digiti的pos更新给原本i位置元素的pos
                    integer[i] = digit2[i].val + '0';//digit的值赋给当前i位置的元素
                    digit2[i].pos = i;//当前替换元素的pos改变

                    digit2[i].used = 1;
                    cnt++;
                    i++;
                    digiti++;
                    continue;
                }
                //如果走到这里 说明虽然是第一位 但是digit中没有比这里更小的
                //需要i++向后挪位,在while最后写了i++这里就不必i++了
            }

            else if(digiti == 0)//i不是n的第一位 digitii却为0 说明一定跳0 并且digitii对应的是0
            {
                //printf("digiti == 0\n");
                if(integer[i] == '0')//是0
                {
                    //printf("integer[i] == '0'\n");
                    //printf("%d\n", findit(integer[i]-'0', i));


                    digit2[findit(integer[i]-'0', i)].used = 1;//将这个0留在原本位置
                    //printf("endfun\n");
                    i++;
                    continue;
                }
                else//不是0
                {
                    //printf("integer[i] != '0'\n");

                    while(digit2[digiti].used == 1)
                        digiti++;

                    integer[digit2[digiti].pos] = integer[i];
                    digit2[findit(integer[i]-'0', i)].pos = digit2[digiti].pos;
                    digit2[digiti].pos = i;
                    integer[i] = digit2[digiti].val + '0';

                    digit2[digiti].used = 1;
                    digiti++;
                    cnt++;
                    i++;
                    if(digit2[digiti].zeroflag == 1)
                    {
                        flag = 0;
                    }
                    continue;
                }
            }

            /*else if(flag)//i!=1 但是出现过跳0 此时已经是n的第三位
            {
                if(digit[digiti].val < integer[i] - '0')
                {
                    printf("i != 0 flag = 1 digit[timpos].val < integer[i] - '0'\n");

                    int tm = 0;
                    while(digit[digiti].used == 1)
                    {
                        if(digit[digiti].zeroflag == 1)
                        {
                            tm = 1;
                            break;
                        }
                        digiti++;
                    }


                    if(!tm)//当前元素未被使用
                    {
                        printf("!tm\n");

                        printf("inte = %s\n", integer);
                        integer[digit[digiti].pos] = integer[i];

                        printf("inte = %s\n", integer);

                        digit[findit((integer[i]-'0'), i)].pos = digit[digiti].pos;//被替换元素pos值更新

                        printf("inte = %s\n", integer);

                        integer[i] = digit[digiti].val + '0';//digit的值赋给当前的元素

                        printf("inte = %s\n", integer);

                        digit[digiti].pos = i;//当前替换元素的pos改变

                        digit[digiti].used = 1;


                        if(digit[digiti+1].zeroflag == 0)
                            digiti++;
                        else
                        {
                            digiti+=2;
                            flag = 0;
                        }
                        cnt++;
                        i++;
                        continue;
                    }

                    flag = 0;
                    digiti++;
                }
            }*/


            else if(digit2[digiti].val < integer[i] - '0')//i!=1 跳零带来的问题也已解决
            {

                //printf("i != 0 flag = 0 digit[cnt].val < integer[i] - '0'\n");

                //printf("digit.val = %d integeri = %d \n",digit[digiti].val , integer[i] - '0');
                while(digit2[digiti].used == 1)
                    digiti++;




                //printf("integer[digit[cnt].pos] = integer[%d] = %c integer[i = %d] = %c\n", digit[cnt].pos, integer[digit[cnt].pos], i, integer[i]);
                integer[digit2[digiti].pos] = integer[i];

                digit2[findit((integer[i]-'0'), i)].pos = digit2[i].pos;//被替换元素pos值更新

                integer[i] = digit2[digiti].val + '0';//digit的值赋给当前的元素
                digit2[digiti].pos = i;//当前替换元素的pos改变
                digit2[digiti].used = 1;

                cnt++;
                i++;
                digiti++;
                continue;
            }

            //printf("一个分支都没走\n");
            digit2[findit(integer[i]-'0', i)].used = 1;
            i++;
        }

        printf("%s %s\n", integer, integermax);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值