题目:
给出n(1<n<10)个数字组成的序列,每次操作可以选取一段连续的区间将这个区间之中的数字放到其他任意位置。问最少经过多少次操作可将序列变为1,2,3……n。
思路:
利用IDA*来求解这个题目,首先每步操作最多可以减少3个位置错误的数字如下图(假设操作之后a、b、c都位于正确的位置)
则启发函数可以为3*cur+h > maxd*3,其中cur是枚举到当前的操作次数,h是序列中位置不对的数字个数,maxd是枚举到的答案。
可以用两重循环枚举选取的区间,然后枚举将其放到的位置,dfs知道序列符合题意。
代码:
#include <bits/stdc++.h> #define inf 0x3f3f3f3f #define MAX 1000000009 #define FRE() freopen("in.txt","r",stdin) #define FRO() freopen("out.txt","w",stdout) using namespace std; typedef long long ll; const int maxn = 10; int n; bool isSrot(int* buf){//判断是不是序列符合题意 for(int i=0; i<n-1; i++){ if(buf[i]>=buf[i+1]) return false; } return true; } int ErrorNum(int* buf){//输出位置不对的数字个数 int cnt = 0; for(int i=0; i<n-1; i++){ if(buf[i]+1!=buf[i+1]) cnt++; } return cnt; } void numSwap(int* buf,int* original,int s,int t,int pos){//将区间放到指定的位置 int idx=0; for(; idx<pos; idx++){ buf[idx] = original[idx]; } for(int i=s; i<t; i++){//将操作的区间放入buf数组 buf[idx++] = original[i]; } for(int i=pos; i<n; i++){//将剩余部分补全 if(i<s || i>=t){ buf[idx++] = original[i]; } } } bool dfs(int cur,int maxd,int* a){ if(3*cur+ErrorNum(a)>3*maxd) return false; if(isSrot(a)) return true; int temp[10]; for(int s=0; s<n; s++){//这两重循环是用来枚举剪切的区间的长度 for(int t=s+1; t<=n; t++){ for(int pos=0; pos<s; pos++){//枚举区间要放入的位置,只要放到s之前就可以枚举所有的情况 //别的地方也不符合实际情况啊,做的时候写成了pos<n,结果一直T numSwap(temp, a, s, t, pos); if(dfs(cur+1, maxd, temp)) return true; } } } return false; } int main(){ //FRE(); int kase = 0,a[10]; while(scanf("%d",&n) && n){ for(int i=0; i<n; i++){ scanf("%d",&a[i]); } for(int i=0; i<=n; i++){ if(dfs(0,i,a)){ printf("Case %d: %d ",++kase,i); break; } } } return 0; }