Power Calculus
不难想到以下剪枝:
-
每次操作新生成的数,当 > n >n >n 时必须执行减法,当 < n <n <n 时必须执行加法,尽量选数值大的数
-
数列中的数必须不重复
-
若每次乘二都不能到达 n n n ,则退出
然而第一种做法无法搜出来答案,主要是在于前面部分可能执行一次减法,而上述方法是先将加法搜完,相当于是搜一个没有答案的很深的子树。实际上关键是在前几次搜索的选择上。考虑换一种搜索顺序,加法和减法同时搜,同时优先搜加法。然而担忧在于初始时分支太多。但是如果不这样做,又不能找到正确答案所在的子树。不过,由于有了第三条剪枝,所以顺利搜出来了。
这个搜索顺序值得玩味。
473
11
1 2 4 8 16 32 64 128 256 192 384
0 1 2 3 4 5 6 7 8 9 10 11
编辑书稿
设层数为k,则总操作数为 56 0 k 560^k 560k
先考虑不加任何优化的暴力+迭代加深。无法通过,层数太深。
用数组来存储空间消耗太大,可以考虑用整数来状压。(然而实测这种方法更慢)
缩小搜索空间的策略有
策略1:只剪切连续的数字片段
策略2:剪切的片段头为a尾为b,要么粘贴到a-1的后面,要么粘贴到b+1前面。
策略3:不要破坏已经连续的片段。
但是策略1和策略2并能保证正解:如5 4 3 2 1 —》 3 2 5 4 1 —》 3 4 1 2 5 -》 1 2 3 4 5。
策略1,2出错主要是因为忽略了后效性,策略3是可以的,把连续的片段看成整体,拆开它一定是比不拆它的步数要少。
可以发现3个策略都只考虑了端点,由于是控制的决策,所以后效性较大。而策略3则在原有状态空间里保留了已经剪切成功的部分。
下面寻找估价函数
由于每次剪切最多更改3个数字的前继(或后继),所以统计前继不对的数字个数为n个那么只少还要搜n/3层。如果d+n/3>maxd就剪枝。
还有一个剪枝是:移动片段b1-b2到b2+1-c后面,相当于移动b2+1-c到b1-b2前面,所以只要枚举把片段往后移动就行了。
最后一个剪枝在于,为了保证相邻两个数的大小关系不被破坏,所以当 a j < a j + 1 a_j<a_{j+1} aj<aj+1 时, a j a_j aj 会跑到后面,可以剪枝。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int n,len,a[15];
//瞎写的
bool dfs(int x,int dep) {
int cnt=0; for(int i=1;i<=n;i++) if(a[i]!=a[i-1]+1) cnt++;
if(cnt==0) return 1;
if(x+(cnt+2)/3>dep) return 0;
int copy[15];copy[0]=copy[n+1]=0;for(int i=1;i<=n;i++) copy[i]=a[i];
for(int i=1;i<=n;i++) {
if(copy[i-1]+1==copy[i]) continue;
for(int j=i;j<=n;j++) {
if(copy[j]+1==copy[j+1]) continue;
if(copy[j]<copy[j+1]) continue;
for(int k=j+1;k<=n;k++) {
if(copy[k]+1==copy[k+1]) continue;
int cnt=0;
for(int s=1;s<i;s++) a[++cnt]=copy[s];
for(int s=j+1;s<=k;s++) a[++cnt]=copy[s];
for(int s=i;s<=j;s++) a[++cnt]=copy[s];
for(int s=k+1;s<=n;s++) a[++cnt]=copy[s];
if(dfs(x+1,dep)) return 1;
}
}
}
for(int i=1;i<=n;i++) a[i]=copy[i];
return 0;
}
signed main() {
// freopen("data6.in","r",stdin);
// freopen("own.out","w",stdout);
int T=0;
while(scanf("%d",&n)&&n) {
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(len=0;len<=n;len++) {
if(dfs(0,len)) {
break;
}
}
printf("Case %d: %d\n",++T,len);
}
}
Derorying the station
不难想到,满足条件的充要条件是最短路长度<=k。
想要满足条件,就必须在最短路上删除一个点。
我们可以以此为搜索框架,进行迭代加深搜索。每次搜索时,一直在最短路上选一个点来删除,直到没有路或路大于等于k。
求最短路时间复杂度是 O ( 50 ) O(50) O(50)。每层搜索最多有50个分支(比直接随便选一个点来好很多,因为有可能白删除了),可以接受。