2018HDU多校5-Problem B. Beautiful Now (hdu 6351)-搜索+剪枝

在这里插入图片描述

题意:

给出一个数,最长有9位,每次可以交换其中两个位置的数,也可以不动,求k次操作后能得到的最大值与最小值

思路:

首先考虑贪心做法,取最大值时每次将最大的数尽可能放在前面,取最小值时每次将最小的数尽可能放在前面,但是我们会发现对于存在很多相同数的情况,会出现错误
例如: k=2时的970979,贪心的求出最大值是999077,但实际上可以达到的最大值是999770

所以我们只能采用搜索的方式,对于本题,我们枚举所有在k次操作内能得到的情况,然后处理出最大值和最小值即可,由之前的贪心策略,我们可以设计一个非常有效剪枝,就是对于一种情况,若其对最大值或最小值都没有贡献,即其的值在最大值和最小值之间,则可以直接将其剪去,因为就算其后续状态可能会对最值产生贡献,但一定比当前状态下直接对最值产生贡献的情况多操作了至少一次,所以它一定不是最优的做法
其实只要这一个剪枝就足够通过本题,但我们仍更多优化方法
判断当前状态是否已经出现过,如果一个状态出现过,则其后续的所有状态都会重复,故剪枝
判断k>=len-1,对于一个长度为len的数,如果修改次数超过len-1次,则一定能修改成最小值和最大值,无需搜索

代码:

#include <iostream>
#include <queue>
#include <set>
#include <algorithm>
using namespace std;

string a;
int k;
string Min="",Max="";

struct nod
{
    string s;         //表示当前状态表示的数字
    int cas;          //表示当前状态剩余可交换次数
};

void bfs()            //暴力搜索枚举所有交换情况
{
    set<string>use;   //判断该情况是否出现过
    queue<nod>Q;
    nod sta;
    use.insert(a);
    int len=a.size();
    sta.s=a;
    sta.cas=k;
    Min=a;
    Max=a;
    Q.push(sta);
    while(!Q.empty())
    {
        nod now=Q.front();
        Q.pop();
        Min=min(Min,now.s);
        Max=max(Max,now.s);
        if(now.cas==0) continue;    //表示无法再多交换

        for(int i=0;i<len;i++)
            for(int j=i+1;j<len;j++)
        {
            if(i==0 && now.s[j]=='0') continue;     //处理前导零
            string tmp=now.s;
            swap(tmp[i],tmp[j]);
            if(tmp<=Max && tmp>=Min)      //贪心策略剪枝,若一种情况对答案没有贡献,则该情况一定不是最优
                continue;
            if(use.count(tmp)==0)        //忽略重复情况
            {
                nod next;
                next.s=tmp;
                next.cas=now.cas-1;
                use.insert(tmp);
                Q.push(next);
            }
        }
    }
}
int main()
{
    std::ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
    {
        cin>>a>>k;
        int len=a.size();
        if(k>=len-1)           //这种情况可直接处理出答案,虽然优化幅度很小,不加也能过...
        {
            Min="";
            Max="";
            sort(a.begin(),a.end());     //从小到大排序后,处理前导零情况,就得到最小值
            int cnt=0;
            for(int i=0;i<len;i++)    //消除前导0
            {
                if(a[i]=='0') cnt++;
                else
                {
                    Min+=a[i];
                    while(cnt>0)
                    {
                        cnt--;
                        Min+='0';
                    }
                }
            }
            reverse(a.begin(),a.end());   //从大到小排序,一定是最大值
            Max=a;
        }
        else
            bfs();
        cout<<Min<<" "<<Max<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43093481/article/details/82861315