bzoj 2824: [AHOI2012]铁盘整理

题目描述

输入输出格式

输入格式:

共两行。第一行为铁盘个数N(1<=N<=50),第二行为N个不同的正整数,分别为从上到下的铁盘的半径R。(1<=R<=100)

输出格式:

一个正整数,表示使铁盘从小到大有序需要的最少翻转次数。

输入输出样例

输入样例#1:
5
2 4 3 5 1
输出样例#1:
5
此题乍一看数据范围极小,自然以为是水题,便想用bfs直接水过去,结果很满意的得了10分,以下是暴力程序:

#include
#include
#include
#include
#include
#include
#include
#define inf 999999999
#define For(i,a,b) for(i=a;i<=b;++i)
#define rep(i,a,b) for(i=a;i>=b;--i)
#define mm(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
int read(){
    int sum=0,flag=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
    while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar();
    return sum*flag;
}
int maxx(int x,int y){
    if(x=0)return x;
    return -x;
}
const int maxn = 100010;
int a[51],b[51];
struct node{
    int s[51],t;
};
int n,cnt;
node tmp;
void bfs(){
    int i,j;
    node T;
    memcpy(T.s,a,sizeof(a));
    T.t=0;
    queueq;
    q.push(T);
    while(!q.empty()){
        T=q.front();
        q.pop();
        int jicun=n;
        while(T.s[jicun]==b[jicun])jicun--;
        For(i,2,jicun){
            memcpy(tmp.s,T.s,sizeof(T.s));
            int k=i>>1;
            For(j,1,k){//直接翻转
                int ch=tmp.s[j];
                tmp.s[j]=tmp.s[i-j+1];
                tmp.s[i-j+1]=ch;
            }
                tmp.t=T.t+1;
                if(memcmp(tmp.s,b,sizeof(b))==0){//如果到达目标状态则结束
                    printf("%d\n",T.t+1);
                    return ;
                }
                q.push(tmp);
        }
    }
}
int main(){
    int i,j;
    n=read();
    For(i,1,n){
        a[i]=read();
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    if(memcmp(a,b,sizeof(b))==0){
        printf("1\n");
        return 0;
    }
    bfs();
    return 0;
}
我发现是状态太多的原因,于是想用trie判重,但是数组小一点便re,大一点就mle,还是十分,仅贴一个程序作为纪念:

#include
#include
#include
#include
#include
#include
#include
#define inf 999999999
#define For(i,a,b) for(i=a;i<=b;++i)
#define rep(i,a,b) for(i=a;i>=b;--i)
#define mm(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
int read(){
    int sum=0,flag=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
    while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar();
    return sum*flag;
}
int maxx(int x,int y){
    if(x=0)return x;
    return -x;
}
const int maxn = 100010;
int a[51],b[51];
struct node{
    int s[51],t;
};
int n,cnt;
int trie[2000000][51];
node tmp;
int pd(){//字典树部分
    int i,u=0,flag=0;
    For(i,1,n){
        if(!trie[u][tmp.s[i]]){
            flag=1;
            trie[u][tmp.s[i]]=++cnt;
        }
        u=trie[u][tmp.s[i]];
    }
    if(flag)return 1;
    return 0;
}
void bfs(){
    int i,j;
    node T;
    memcpy(T.s,a,sizeof(a));
    T.t=0;
    queueq;
    q.push(T);
    while(!q.empty()){
        T=q.front();
        q.pop();
        For(i,2,n){
            memcpy(tmp.s,T.s,sizeof(T.s));
            int k=i>>1;
            For(j,1,k){
                int ch=tmp.s[j];
                tmp.s[j]=tmp.s[i-j+1];
                tmp.s[i-j+1]=ch;
            }
            if(pd()){//判重
                tmp.t=T.t+1;
                if(memcmp(tmp.s,b,sizeof(b))==0){
                    printf("%d\n",T.t+1);
                    return ;
                }
                q.push(tmp);
            }
        }
    }
}
int main(){
    int i,j;
    n=read();
    For(i,1,n){
        a[i]=read();
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    if(memcmp(a,b,sizeof(b))==0){
        printf("1\n");
        return 0;
    }
    bfs();
    return 0;
}
我开始认真对待这道题,一些题解里说最多可以只翻2*n次但都没有解释清楚,但身为蒟蒻的我一时半会儿想不通,以下就写出证明过程以供其他蒟蒻参考:

每一次确定最末尾的那个数,步骤是第一步,把最末尾未确定的数换到第一个位置,接下来就可以换到最后了,接下来一样的步骤,举个例子:

对于 2 4 3 5 1,最开始要确定最后一位,也就是要把最后一位变成5,于是先把5放到第一位,->5 3 4 2 1,再换到最后1 2 4 3 5,之后5就不管他了

只剩下1 2 4 3,于是一样的->4 2 1 3->3 1 2 4,还有3个数,3 1 2->2 1 3->1 2 3于是就排好了,算下来,最坏情况只需要2*n-1步

于是就有了第一个剪枝(大于2*n-1的情况可以直接减掉),然而有了这个剪枝还是没有什么用,我们还要利用另外一个原理:若两个数在目前位置上相邻,但在目标位置上不相邻,则因为是翻转,因此至少需要一步把这两个数分开,因此从当前排列到目标排列最少就需要出现这种情况的总和。待会代码里还会解释:

#include
#include
#include
#include
#include
#include
#include
#define inf 999999999
#define For(i,a,b) for(i=a;i<=b;++i)
#define rep(i,a,b) for(i=a;i>=b;--i)
#define mm(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
int read(){
    int sum=0,flag=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
    while(c>='0'&&c<='9')sum=sum*10+c-'0',c=getchar();
    return sum*flag;
}
int maxx(int x,int y){
    if(x=0)return x;
    return -x;
}
const int maxn = 100010;
struct node{
	int value,pos,nowpos;
};
node a[maxn];
bool cmp(node c,node d){
	return c.value
剪枝万岁!

你可能感兴趣的:(dfs)