zoukankan      html  css  js  c++  java
  • Jzoj5441【NOIP2017提高A组冲刺11.1】序列

    给定一个1~n的排列x,每次你可以将x1~xi翻转。你需要求出将序列变为升序的最小操作次数。有多组数据。

    此题十分不友善

    对于多年没有打过搜索的蒟蒻更是如此

    (强行)假定这个题数据范围是在坑人(因为我以前真的见过有人二分图匹配的题正解是状压DP的)

    开始寻找多项式算法。。。。。

    发现各种贪心都不行。。。。。

    实在不行开始写暴力,最后挂掉0分


    说说正解:迭代加深+剪枝

    我们考虑两个东西,深度上限:2n-2 这个非常显然(其实还有一种说法是上界为n但是没有人证明)

    估价函数f(S),表示S到结果至少需要多少步

    我们令 f(S)=Σ[abs(S[i]-S[i+1])>1]+已用的步数和步数上限做比较如果大于直接退出

    实现f非常简单,我们考虑为什么这样是对的

    从各种贪心和样例来看,我们发现若干个连续的数字在最优解中肯定不会被破坏(误,其实我也不知道为什么)

    引用题解:

    我们发现每次翻转只会改变一对相邻数对,因此对于一个状态求出相差>1 的相邻数对的数量,剩余步数一定大于这个值。加上这个剪枝就能通过本题。

    假装这个是显然的,我们就可以迭代加深了

    700ms+,十分菜

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    int a[50],b[50],n,m;
    bool dfs(int p,int d){
    	if(d+p>m) return 0;
    	for(int i=1;i<=n;++i) if(a[i]!=i) goto next;
    	return 1; next:
    	for(int q,i=2;i<=n;++i){
    		q=d+(i<n&&abs(a[i]-a[i+1])==1)-(i<n&&abs(a[1]-a[i+1])==1);
    		reverse(a+1,a+i+1);
    		if(dfs(p+1,q)) return 1;
    		reverse(a+1,a+i+1);
    	}
    	return 0;
    }	
    int aim(){
    	int p=0;
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i) scanf("%d",a+i);
    	for(int i=1;i<n;++i) if(abs(a[i]-a[i+1])>1) ++p;
    	int l=0,r=n<<1; memcpy(b,a,n+3<<2);
    	for(;l<r;){
    		m=l+r>>1;
    		if(dfs(0,p)) r=m;
    		else l=m+1;
    		memcpy(a,b,n+3<<2);
    	}
    	printf("%d
    ",l);
    }
    int main(){	
    	freopen("sequence.in","r",stdin);
    	freopen("sequence.out","w",stdout);
    	int T;
    	for(scanf("%d",&T);T--;aim());
    }

  • 相关阅读:
    selenium实战脚本集(2)——简单的知乎爬虫
    selenium实战脚本集(1)——新浪微博发送QQ每日焦点
    使用swift和rails来实现ios账号系统
    一段js代码
    你应该学会使用的5个ruby方法
    小而美的ghost driver
    还没被玩坏的robobrowser(8)——robobrowser的实现原理
    还没被玩坏的robobrowser(7)——表单操作
    还没被玩坏的robobrowser(6)——follow_link
    还没被玩坏的robobrowser(5)——Beautiful Soup的过滤器
  • 原文地址:https://www.cnblogs.com/Extended-Ash/p/9477258.html
Copyright © 2011-2022 走看看