• 排序:搜索/组合数


    题目传送门(loj)

    看到我这个标题其实就想吐槽了。搜索?

    其实是个水题,但放到考试里就不水了。

    看完题目,扑面而来的二进制气息,让人嗅到线段树的气味

    扯淡!虽说的确有人打了一个线段树。

    对于30%的数据,n<=4,纯暴力,枚举所有操作,枚举所有的可能位置。

    n>5输出n!或(n-1)!能骗15分直接让分数增加一半

    咳咳,说正解,对于全部数据1<=n<=12,其实只需要剪枝

    我们先做小操作,后做大操作,考虑下面这种情况:

    8 7 5 6 4 3 2 1,目前在做最小的操作(即1换1)

    我们必须在这一步让{8,7},{5,6},{4,3}和{2,1}这两个区间都连续且有序(即是以1为公差的等差数列)

    因为以后的每次操作都会把它们看作一个整体,它们的相对位置再也不会改变

    显然不能一步做到改变3个或更多区间。方案数是0,return掉。

    结论1:当非连续递增区间超过2个时,没有可行方案,直接return。

    再考虑下面这个:8 1 5 6 3 4 7 2,在第一步最小操作

    需要调整的是{8,1}和{7,2}两个部分,有4种操作:换78,换12,换17,换28

    都试一试吧。在更加复杂的情况下并不好判断,都是一下无伤大雅。

    如果你想卡常,显然可以从这里剪更多的枝,但没必要了。

    结论2:当非连续递增区间为2个时,分4种情况向下搜索(交换{1前2后,1前2前,1后2后,1后2前}半部分)。

    再考虑这个:5 6 7 8 3 4 1 2,在第二步2换2的操作了

    需要调整的只有{3,4,1,2}一个部分

    显然喽,交换{3,4}和{1,2}呗。

    对于更一般的情况,就是交换这唯一一个需要调整的部分的前后半段。

    结论3:当非连续递增区间为1个时,交换其自身的前后半段向下搜索。

    最后放松一下我们来考虑这个:1 2 3 4 5 6 7 8,你管他是第几步操作呢!

    什么都不换显然是最好的选择,换完了以后也救不回来,直接向下搜就可以了。

    结论4:当非连续递增区间为0个时,直接向下搜索。

    这四个结论就已经囊括了所有的搜索过程中可能出现的情况,可以搜索了。

    最后一种操作完成时累加答案。

    因为问题是操作序列而不是操作组合所以答案累加的是操作数的阶乘。

    可能不止一种操作组合合要求所以不要直接找到一种就exit(0)!

    真的没什么了,需要我教你打dfs么?

     1 #include<cstdio>
     2 #include<cstdlib>
     3 using namespace std;
     4 inline void swap(int &a,int &b){int t=a;a=b;b=t;}
     5 int n,a[5025],ans,anss,pow[13]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096},fac[13]={1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600};
     6 void dfs(int st,int step){
     7     if(st==n){for(int i=1;i<=pow[n];++i)if(a[i]!=i)return;anss+=fac[step];return;}
     8     int fault=0,f[4]={0,0,0,0};
     9     for(int i=1;i<=pow[n];i+=pow[st+1]){
    10         int maxn=0;
    11         for(int j=i+1;j<i+pow[st+1];++j)
    12             if(a[j]!=a[j-1]+1){f[++fault]=i;break;}
    13         if(fault==3)break;
    14     }
    15     if(fault==1){
    16         for(int i=f[1];i<f[1]+pow[st];++i)swap(a[i],a[i+pow[st]]);
    17         dfs(st+1,step+1);
    18         for(int i=f[1];i<f[1]+pow[st];++i)swap(a[i],a[i+pow[st]]);
    19     }
    20     else if(fault==2){
    21         for(int i=0;i<pow[st];++i)swap(a[f[1]+i],a[f[2]+i]);
    22         dfs(st+1,step+1);
    23         for(int i=0;i<pow[st];++i)swap(a[f[1]+i],a[f[2]+i]);
    24         for(int i=0;i<pow[st];++i)swap(a[f[1]+i+pow[st]],a[f[2]+i+pow[st]]);
    25         dfs(st+1,step+1);
    26         for(int i=0;i<pow[st];++i)swap(a[f[1]+i+pow[st]],a[f[2]+i+pow[st]]);
    27         for(int i=0;i<pow[st];++i)swap(a[f[1]+i+pow[st]],a[f[2]+i]);
    28         dfs(st+1,step+1);
    29         for(int i=0;i<pow[st];++i)swap(a[f[1]+i+pow[st]],a[f[2]+i]);
    30         for(int i=0;i<pow[st];++i)swap(a[f[1]+i],a[f[2]+i+pow[st]]);
    31         dfs(st+1,step+1);
    32         for(int i=0;i<pow[st];++i)swap(a[f[1]+i],a[f[2]+i+pow[st]]);
    33     }
    34     else if(fault==0)dfs(st+1,step);
    35 }
    36 int main(){
    37     scanf("%d",&n);
    38     for(int i=1;i<=pow[n];++i)scanf("%d",&a[i]);
    39     dfs(0,0);
    40     printf("%d",anss);
    41 }
    这么水的题考试居然没A。。。
  • 相关阅读:
    bfs两种记录路径方法
    次小生成树
    2018 ICPC 区域赛 焦作场 D. Keiichi Tsuchiya the Drift King(计算几何)
    数组分组
    POJ
    数位DP详解
    2018ICPC青岛 E
    HDU
    Google工程师打造Remix OS系统 桌面版安卓下载
    使用angular封装echarts
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11160580.html
Copyright © 2020-2023  润新知