首页 > 编程语言 >算法竞赛进阶指南 0x24 迭代加深

算法竞赛进阶指南 0x24 迭代加深

时间:2022-09-24 12:01:17浏览次数:76  
标签:return 进阶 迭代 int sum 0x24 搜索 dfs path

对于深度优先,如果答案在很浅的部位,但是整个搜索树过于深,那么就会寄掉。

但是对于广度优先,本来挺好,但是在队列里面存储太多的元素,到时爆。同时,广度优先也不容易存储数据。

对于迭代加深,就是限定层数进行搜索。当这一层没有答案的时候,把限定的层数增加,再次搜索。

  1. 有着广搜的搜索浅层的时间复杂度
  2. 不会爆空间
  3. 容易利用系统所给的栈来进行存储

AcWing170. 加成序列

满足如下条件的序列 X(序列中元素被标号为 123m)被称为“加成序列”:

  1. X[1]=1
  2. X[m]=n
  3. X[1]<X[2]<<X[m1]<X[m]
  4. 对于每个 k2km)都存在两个整数 ij1i,jk1ij 可相等),使得X[k]=X[i]+X[j]1. 。

你的任务是:给定一个整数 n,找出符合上述条件的长度 m 最小的“加成序列”。

如果有多个满足要求的答案,只需要找出任意一个可行解。

输入格式

输入包含多组测试用例。

每组测试用例占据一行,包含一个整数 n

当输入为单行的 0 时,表示输入结束。

输出格式

对于每个测试用例,输出一个满足需求的整数序列,数字之间用空格隔开。

每个输出占一行。

数据范围

1n100

输入样例:

5
7
12
15
77
0

输出样例:

1 2 4 5
1 2 4 6 7
1 2 4 8 12
1 2 4 5 10 15
1 2 4 8 9 17 34 68 77

答案在浅层,因为通过[1, 2, 4, 8, 16, 32, 64, 128],可能的情况的层数少于8层。但是如果直接dfs,可能会整一个100层。

优化:

  1. 优化搜索的顺序
  2. 对重复的情况进行排除冗余等效。
#include <bits/stdc++.h>
using namespace std;
#define N 120
int n;
int path[N];

bool dfs(int u, int k)//u表示当前是多少层,k表示当最多搜索k-1层
{
    if(u == k) return path[u-1] == n;

    bool v[N] = {0};//注意初始化
    for(int i = u-1; i > 0; i--)//从大到小进行枚举(优化搜索顺序)
        for(int j = i; j > 0; j--)//int j = i可以减少重复
        {
            int sum = path[i] + path[j];
            if(sum <= path[u-1] || sum > n || v[sum]) continue;
            v[sum] = true;//排除等效冗余
            path[u] = sum;
            if(dfs(u+1, k))return true;
        }


    return false;
}
int main()
{
    while(cin >> n, n)
    {
        // if(n == 1) {
        //     puts("1");
        //     continue;
        // }
        int k = 2; //表示最多的层数(共搜索k-1层)
        /*
        DEBUG:
        当 k的初始值是1的话,由于我从u == 2开始,所以迭代加深并没有起到任何的作用,所以会直接把所有的界定啊全部搞一遍。。。
        */
        path[1] = 1;
        while(!dfs(2, k)) k++;
        for(int i = 1; i < k; i++) printf("%d ", path[i]);
        puts("");
    } 
    return 0;
}

AcWing171. 送礼物

达达帮翰翰给女生送礼物,翰翰一共准备了 N 个礼物,其中第 i 个礼物的重量是 G[i]。

达达的力气很大,他一次可以搬动重量之和不超过 W

的任意多个物品。

达达希望一次搬掉尽量重的一些物品,请你告诉达达在他的力气范围内一次性能搬动的最大重量是多少。

输入格式

第一行两个整数,分别代表 WN

以后 N 行,每行一个正整数表示 G[i]。

输出格式

仅一个整数,表示达达在他的力气范围内一次性能搬动的最大重量。

数据范围

1N46,

1W,G[i]2311

输入样例:

20 5
7
5
4
18
1

输出样例:

19

“降维打击”

这一道题目其实可以使用背包问题来做,先降维打击:

#include <bits/stdc++.h>
using namespace std;
#define N 100
int w[N], v[N];
int n, vol;
int dp[N];
int main()
{
    cin >> vol >> n;
    for(int i = 1; i <= n; i++)
    {
        int x;
        scanf("%d", &x);
        v[i] = x;
        w[i] = x;
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = vol; j > 0; j--)
        {
            if(j- w[i] > 0)
            {
                dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
            }
        }
    }
    cout << dp[vol];
    return 0;
}

但是出事了:

  1. 背包的时间复杂度是 \(m* n\)
  2. 由于m过大,会爆内存。

所以一定要看清楚题目!

暴搜练习生:

#include <bits/stdc++.h>
using namespace std;
#define N 10
int path[N];
void dfs(int u, int k)
{
    if(u == k)
    {
        for(int i = 1; i <= u-1; i++) printf("%d", path[i]);
        putchar('\n');
        return;
    }
    path[u] = 0;
    dfs(u+1, k);
    path[u] = 1;
    dfs(u+1, k);
}
int main()
{
    int xx = 5;//打印4位依次递增的二进制数(实现以递归形式写出来了暴力破译)
    dfs(1, xx);
    return 0;
}

还有一种情况是使用一个数字每次加一,然后通过每一位是不是一来决定对应的情况。

正二八经

这一道题目采用双向搜索,先搜索前一半,然后再搜索后一半。

计算集合内元素相加不超过某一个子的方法

  1. 两个都排序,然后从第一个里面从大到小,从第二个里面从小到大
  2. 一个排序,然后遍历另一个里面的数字,通过二分确定最大值。

代码实现

#include <bits/stdc++.h>
using namespace std;
#define N 50
int n, m;
typedef long long ll;
ll g[N];//表示礼物的重量
ll ans = 0;
ll a[1 << 25];
int cnt = 0;//作为a的计数
void dfs_1(int u, int k, ll sum)
{
    if(u == k) {
        a[++cnt] = sum;
        return;
    }
    if(sum + g[u] <= m)
    {
        dfs_1(u+1, k, sum+g[u]);//选择这一个
    }
    dfs_1(u+1, k, sum);//不选择这一个
}
void dfs_2(int u, int k, ll sum)
{
    if(u == k) {
        int l = 1, r = cnt;
        while(l < r)
        {
            int mid = (l+r+1) >> 1;
            if(a[mid] + sum <= m) l = mid;
            else r = mid - 1;
        }
        if(a[l] + sum > ans) ans = a[l] + sum;
        return;
    }
    if(sum+g[u] <= m) dfs_2(u+1, k, sum + g[u]);
    dfs_2(u+1, k, sum);
}
int main()
{
    cin >> m >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> g[i];
    }
    //cout << "ok";
    sort(g+1, g+1+n);
    reverse(g+1, g+1+n);//注意这里的这一个优化:从大到小进行搜索
    dfs_1(1, (n+1)/2, 0);
    sort(a+1, a+cnt+1);
    cnt = unique(a+1, a+cnt+1) - (a+1);
    dfs_2((n+1)/2, n+1, 0);
    cout << ans;
    return 0;
}

标签:return,进阶,迭代,int,sum,0x24,搜索,dfs,path
From: https://www.cnblogs.com/xjsc01/p/16725304.html

相关文章

  • python进阶之路3
    内容概要pycharm下载与使用python语法之注释python语法之变量与常量python基本数据类型(先大致了解有哪些)pycharm下载与使用1.该软件分为收费版和免费版 免费版本功......
  • Three.js进阶之旅:基础介绍(二)
    本文为稀土金块技术社区的第一篇署名文章。14日内禁止转载,14日后禁止擅自转载。侵权必究!概括专栏上一篇《Three.js进阶之旅:基础介绍(上)》主要解释三.js环境建设......
  • 实例85 牛顿迭代法求解方程
    #include<stdio.h>#include<math.h>#include<stdlib.h>intFunction(double,double*,double*);intNewton(double*,double,int);intFunction(x,f,dy)dou......
  • 二叉树遍历(递归、迭代)
    前中后序遍历递归法//前序遍历varpreorderTraversal=function(root){letres=[];constdfs=function(root){  if(root===null)return;  //先序遍历所......
  • VeighNa 学习进阶(二)
    今天学了一下连接行情,再次吐槽那个文档,写的什么玩意?是作者故意为之?然后拿example中的no_ui里面的run.py学习了一下,感觉作者写代码的习惯和思维非常奇怪比如在这句中: m......
  • Java流程控制02(Scanner进阶)
    Scanner进阶使用判断输入的是否为整数:packageScanner;importjava.util.Scanner;publicclassDemo03{publicstaticvoidmain(String[]args){S......
  • Python进阶篇03-内置函数
    内置函数abs()返回数字的绝对值语法:abs(x),x为数值表达式:整数、浮点数、复数返回值:返回x的绝对值,若x为复数,则返回复数的大小>>>abs(-5)5>>>abs(-12.27)12.27>>>a......
  • python进阶——装饰器
    万物皆对象介绍装饰器之前,我们需要理解一个概念:在介绍装饰器前,我们需要理解一个概念:在Python开发中,一切皆对象。什么意思呢?就是我们在开发中,无论是定义的变量(数字、字......
  • JAVA中容器设计的进化史:从白盒到黑盒,再到跻身为设计模式之一的迭代器
    大家好,又见面了。在我们的项目编码中,不可避免的会用到一些容器类,我们可以直接使用List、Map、Set、Array等类型。当然,为了体现业务层面的含义,我们也会根据实际需要自行封......
  • C语言进阶-动态内存管理
    C语言进阶之动态内存管理前言C语言一、为什么存在动态内存分配我们已经掌握的内存开辟方式有:intval=20;//在栈空间上开辟四个字节chararr[10]={0};//在栈空间......