zoukankan      html  css  js  c++  java
  • 一些“基础”算法

    一些“基础”算法

    枚举子集的子集

    给定n个元素,问这n个元素组成的每一个集合的所有子集。

    for(int S = 1; S < (1 << n); ++S) {
    	for(int S1 = S; S1 != 0; S1 = (S1 - 1) & S) {
    		//do something
    	}
    } 
    

    最外层就是(O(2^n))枚举所有集合。如果要按普通方法枚举子集,应该将(S1)(S)开始每次减一,再判断合法性。然而由于&(S)的结果只减不增,(S1)可以通过(-1)然后&(S)来直接到达最近合法状态。

    复杂度不会证,但是知道是(O(3^n))

    更详细的讲解

    搜索

    由于万恶的老师没有讲基本的搜索,于是我这里也跳过啦 XD

    迭代加深搜索

    如果搜索树的深度不确定,那么可以使用迭代加深搜索((iterative deepening)

    1. 枚举(maxd)表示最深枚举深度
    2. 假设当前深度为(g(n)),乐观估计至少要(h(n))层才能到达叶结点,那么(g(n)+h(n)>maxd)时,应该剪枝,这就是(IDA*)(基于迭代加深的(A*)算法。)

    埃及分数问题

    传送门

    给出一个分数,要求求出最少的x分之1的形式,如果个数相同,要求最小的数字最大。

    考虑搜索,因为层数不定,所以使用迭代加深搜索

    细节问题需要注意

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <string>
    #define ll long long
    using namespace std;
    
    ll a, b, ans[15], tmp[15], now, INF = 2147483647;
    
    ll gcd(ll x, ll y) {
    	if(y == 0) return x;
    	return gcd(y, x % y);
    }
    
    int flag;
    
    void dfs(ll dep, ll na, ll nb) {
    	if(dep > now) return;
    	if(na == 1 && nb > tmp[dep - 1]) {
    		tmp[dep] = nb;
    		if(!flag || tmp[dep] < ans[dep]) {
    			memcpy(ans, tmp, sizeof(tmp));
    		}
    		flag = 1;
    		return;
    	}
    	ll st = max(nb / na, tmp[dep - 1] + 1), ed = (now - dep + 1) * nb / na;
    	if(ed > INF) ed = INF - 1;
    	if(flag && ed >= ans[now]) ed = ans[now] - 1;
    	for(ll i = st; i <= ed; i++) {
    		tmp[dep] = i;
    		ll ty = gcd(na * i - nb, nb * i);
    		dfs(dep + 1, (na * i - nb) / ty, nb * i / ty);
    	}
    }
    
    int main() {
    	scanf("%lld%lld", &a, &b);
    	ll c = gcd(a, b);
    	a /= c, b /= c;
    	if(a == 1) {
    		cout << b << '
    ';
    		return 0;
    	}
    	tmp[0] = 1;
    	for(now = 1; ;now++) {
    		dfs(1, a, b);
    		if(flag) {
    			for(int i = 1; i <= now; i++) {
    				cout << ans[i] << " ";
    			}
    			return 0;
    		}
    	}
    	return 0;
    }
    

    A*

    我们如果把“当前状态乐观估计还要(h(n))层才能到达终点”这个(idea)用到(bfs)上,会不会有好效果?这就是(A*)

    例如在(dijkstra)算法中,我们每次取的是(d(v))最小的点。如果我们加上(A*)的思想,就可以每次取(d(v)+h(v))最小的点。(例如说这里的(h(v))可以是连接(v)的最小的边)
    我们可以把常规(bfs)用的队列改成优先队列,每次选(g(n)+h(n))最小的点来更新。

    万圣节后的早晨

    传送门

    一个地图,有障碍不能走,要求所有小写字母到达自己对应的大写字母

    这题可以用(bfs)来写,也可以加入(A*),每个状态的(H(n))可以估计为每个小写字母到大写字母的最短路之和。

    代码先鸽着

    练习题

    hdu2234

    双向搜索

    双向搜索又名折半搜索。当搜索的复杂度在指数级的时候,我们可以通过将指数折半的方法降低搜索复杂度。

    具体做法是从初态和终态出发各搜索一半状态,产生两颗深度减半的搜索树,两颗树交汇在一起形成最终答案,将(O(n^k))降低到(O(n^{k/2}+n^{k/2+1}))的复杂度。

    和为0的四个值

    传送门

    给定的各有n个整数的数列(A)(B)(C)(D),从每个数列中取一个数使得四个数加起来等于(0),问这样的方案数。

    (n^4)的枚举肯定会超时,所以我们先(n^2)处理(A[i] + B[i])的值,并把它加到(sum)数组中,然后对(sum)数组进行排序,然后寻找每一组((-C[i] - D[j]))(sum)出现了多少次,把这些次数都加起来,相加后的结果即是答案

    话说根本用不到搜索的hhhh

    #include<bits/stdc++.h>
    #define N 4005
    #define LL long long
    using namespace std;
    
    int T,n,A[N],B[N],C[N],D[N],sum[N*N];
    
    int main() {
    	scanf("%d", &T);
    	while(T--) {
    		scanf("%d", &n);
    		for(int i = 0; i < n; i++) {
    			scanf("%d%d%d%d", &A[i], &B[i], &C[i], &D[i]);
    		}
    		int c = 0;
    		for(int i = 0; i < n; i++) {
    			for(int j = 0; j< n; j++) {
    				sum[c++] = A[i] + B[j];
    			}
    		}
    		stable_sort(sum, sum + c);
    		LL ans=0;
    		for(int i = 0; i < n; i++) {
    			for(int j = 0; j < n; j++) {
    				ans += upper_bound(sum, sum + c,-C[i]-D[j]) - lower_bound(sum, sum + c, -C[i]-D[j]);
    			}
    		}
    		printf("%lld
    ", ans);
    		if(T) printf("
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    redis数据查看工具
    CSS3 使用 calc() 计算高度 vh px
    springboot集成freemarker属性配置(不知道是针对于某个版本,2.0后有变动)
    springboot中Thymeleaf和Freemarker模板引擎的区别
    Velocity与Jsp、Freemarker的对比
    Eclipse中如何安装Git插件
    git 介绍
    PO BO VO DTO POJO DAO 概念及其作用
    layer icon样式及 一些弹框使用方法
    layer之关闭弹窗
  • 原文地址:https://www.cnblogs.com/loceaner/p/11346327.html
Copyright © 2011-2022 走看看