共两行。第一行为铁盘个数N(1<=N<=50),第二行为N个不同的正整数,分别为从上到下的铁盘的半径R。(1<=R<=100)
输出格式:一个正整数,表示使铁盘从小到大有序需要的最少翻转次数。
5 2 4 3 5 1
5
#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
剪枝万岁!