C++解题报告 : 迭代加深搜索之 ZOJ 1937 Addition Chains


此题不难,主要思路便是IDDFS(迭代加深搜索),关键在于优化。
一个IDDFS的简单介绍,没有了解的同学可以看看:
https://www.cnblogs.com/MisakaMKT/articles/10767945.html
我们可以这么想,设当前规定长度为M,题目要求得出的数为N。
在搜索中,当前的步数为step,当前的数列为 数组a。
首先来确定思路,便是在以得出的数列a中枚举每两个数相加得出sum,然后继续搜索下一步。
初步的代码便是:

void iddfs(int x) {
    for(int i=1;i<=step;i++)
        for(int j=1;j<=step;j++) {
            a[step+1]=a[i]+a[j];
            iddfs(step+1);
        }
}

但是我们需要保证的数列应该是有序上升的,所以需要保证a[step+1]必须大于a[x]。

void iddfs(int x) {
    for(int i=1;i<=step;i++)
        for(int j=1;j<=step;j++) {
            a[step+1]=a[i]+a[j];
            if(a[step+1]>a[step]) continue;
            iddfs(step+1);
        }
}

但这样还不够,为了满足样例的需求,应该要从大到小来枚举加数。为了避免重复搜,还可以让j=i。

void iddfs(int x) {
    for(int i=step;i>=1;i--)
        for(int j=i;j>=1;j--) {
            a[step+1]=a[i]+a[j];
            if(a[step+1]>a[step]) continue;
            iddfs(step+1);
        }
}

现在可以发现可以简单的过样例了,但最后一个样例的时间却非常的长。所以我们应该要思考优化了。
可以发现序列的最后一个数最大都只能是\(a_{step}*2^{M-step}\)。为什么呢,因为要使最后结果最大,选的都必须是序列中最大的两个数,也就是最后一个数。结果算出来便就是\(a_{step}*2^{M-step}\)。
那么我们的优化就很简单了,如果\(a_{step}*2^{M-step}\)是小于N的,那就根本不可能有解,就需要舍去。这便是这道题剪枝的思想。
最后的代码:

#include <iostream>
#include <cstring>
using namespace std;

#define N 200

int a[200],n,len,flag;

void dfs(int step) {
    if(step>len) return ;
    if(step==len && a[step]==n) {//找到了解,输出
        for(int i=1;i<=step;i++)
            printf("%d ",a[i]);
        puts("");
        flag=1;
        return ;
    }
    if(a[step]>=n) return ;
    for(int i=step;i>=1;i--)
        for(int j=step;j>=i;j--) {
            if(a[i]+a[j]>a[step] && a[i]+a[j]<=n ) {
                a[step+1]=a[i]+a[j];
                int sum=a[i]+a[j];
                for(int k=step+2;k<=len;k++)
                    sum*=2;
                if(sum<n) continue;
                dfs(step+1);
                if(flag) return ;
            }
        }
}

int main() {
    while(cin>>n) {
        len=0;
        if( !n ) return 0;
        memset(a,0,sizeof(0));
        a[1]=1;a[2]=2,a[3]=4;
        int m=1;
        while(m<n) {//这句加不加都无所谓,对时间复杂度影响不大
            m*=2;//len完全可以从1开始
            len++;
        }
        for(len;;len++) {
            dfs(1);
            if(flag) break;
        }
        flag=0;
    }
}

include

include

using namespace std;

define N 200

int a[200],n,len,flag;

void dfs(int step) {
if(step>len) return ;
if(step==len && a[step]==n) {
for(int i=1;i<=step;i++)
printf("%d ",a[i]);
puts("");
flag=1;
return ;
}
if(a[step]>=n) return ;
for(int i=step;i>=1;i--)
for(int j=step;j>=i;j--) {
if(a[i]+a[j]>a[step] && a[i]+a[j]<=n ) {
a[step+1]=a[i]+a[j];
int sum=a[i]+a[j];
for(int k=step+2;k<=len;k++)
sum*=2;
if(sum<n) continue;
dfs(step+1);
if(flag) return ;
}
}
}

int main() {
while(cin>>n) {
len=0;
if( !n ) return 0;
memset(a,0,sizeof(0));
a[1]=1;a[2]=2,a[3]=4;
int m=1;
while(m<n) {
m*=2;
len++;
}
for(len;;len++) {
dfs(1);
if(flag) break;
}
flag=0;
}
}

原文地址:https://www.cnblogs.com/MisakaMKT/p/10768078.html

时间: 2024-10-11 23:08:21

C++解题报告 : 迭代加深搜索之 ZOJ 1937 Addition Chains的相关文章

UVA 1343 - The Rotation Game-[IDA*迭代加深搜索]

解题思路: 这是紫书上的一道题,一开始笔者按照书上的思路采用状态空间搜索,想了很多办法优化可是仍然超时,时间消耗大的原因是主要是: 1)状态转移代价很大,一次需要向八个方向寻找: 2)哈希表更新频繁: 3)采用广度优先搜索结点数越来越多,耗时过大: 经过简单计算,最长大概10次左右的变换就能出解,于是笔者就尝试采用IDA*,迭代加深搜索的好处是: 1)无需存储状态,节约时间和空间: 2)深度优先搜索查找的结点数少: 3)递归方便剪枝: 代码如下: 1 #include <iostream> 2

vijos1308 埃及分数(迭代加深搜索)

题目链接:点击打开链接 题目描述: 在古埃及,人们使用单位分数的和(形如1/a的, a是自然数)表示一切有理数.如:2/3=1/2+1/6,但不允许2/3=1/3+1/3,因为加数中有相同的.对于一个分数a/b,表示方法有很多种,但是哪种最好呢?首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越好. 如:19/45=1/3 + 1/12 + 1/180 19/45=1/3 + 1/15 + 1/45 19/45=1/3 + 1/18 + 1/30, 19/45=1/4 + 1/6

UVA 11212 Editing a Book [迭代加深搜索IDA*]

11212 Editing a Book You have n equal-length paragraphs numbered 1 to n. Now you want to arrange them in the order of 1, 2, . . . , n. With the help of a clipboard, you can easily do this: Ctrl-X (cut) and Ctrl-V (paste) several times. You cannot cut

UVA-11214 Guarding the Chessboard (迭代加深搜索)

题目大意:在一个国际象棋盘上放置皇后,使得目标全部被占领,求最少的皇后个数. 题目分析:迭代加深搜索,否则超时. 小技巧:用vis[0][r].vis[1][c].vis[2][r+c].vis[c-r+N]分别标志(r,c)位置相对应的行.列.主.副对角线有没有被占领(详见<入门经典(第2版)>P193),其中N表示任意一个比行数和列数都大(大于等于)的数. 代码如下: # include<iostream> # include<cstdio> # include&l

USACO/fence8 迭代加深搜索+剪枝

题目链接 迭代加深搜索思想. 枚举答案K,考虑到能否切出K个木头,那么我们当然选最小的K个来切. 1.对于原材料,我们是首选最大的还是最小的?显然,首选大的能够更容易切出,也更容易得到答案. 2.对于目标木头,我们是优先得到最大的还是最小的?显然,由于K个木头我们都要得到,那么当然先把最大的(最难得到的)先得到,这种搜索策略更优. 3.假设总原材料为all,前K个木头总和为sum,那么all-sum就是这一次切割过程中能[浪费]的最大数目.对于一个切剩下的原材料,若它比最小的目标木头还要小,则它

hdu 1560 DNA sequence(迭代加深搜索)

DNA sequence Time Limit : 15000/5000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 15   Accepted Submission(s) : 7 Font: Times New Roman | Verdana | Georgia Font Size: ← → Problem Description The twenty-first century

【迭代加深搜索】埃及分数问题

谢谢阿苏~http://blog.csdn.net/urecvbnkuhbh_54245df/article/details/5856756 [迭代加深搜索(ID,iterative deepening)]:从小到大枚举上限maxd,每次执行只考虑深度不超过maxd的结点. ------对于可以用回溯法求解但解答树的深度没有明显上限的题目,可以考虑ID算法: ------优点:它主要是在递归搜索函数的开头判断当前搜索的深度是否大于预定义的最大搜索深度,如果大于,就退出这一层的搜索,如果不大于,就

hdu 1560 迭代加深搜索

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1560 只能说bin神太给力了.. 又学到不少新知识.. 迭代加深搜索,貌似 又叫IDA*, 就是给搜索深度一个限制,搜索到一个满足条件就结束. 注意剪枝~ 代码: #include <iostream> #include <cstdio> #include <cstring> using namespace std; char g[10][10]; int size[10];

【wikioi】2495 水叮当的舞步(A*+迭代加深搜索)

这题我还是看题解啊囧.(搜索实在太弱.完全没想到A*,还有看题的时候想错了,.,- -) 好吧,估价还是那么的简单,判断颜色不同的数目即可(左上角的联通块不算在内) 然后A*还是一样的做法. 迭代加深还是一样的味道- 在这里我们用c[i][j]来表示左上角开始的联通块和联通块外面一层(因为要从外面一层拓展颜色),分别记为1和2 那么我们在搜索的时候,染色只要染c[i][j]为2的颜色种类,并且更新联通块(在这里不需要传图,因为一层一层的拓展下去的话,是单调递增的,所以用不到之前的颜色)我们在搜索