数据结构与算法——前缀树和贪心算法(2)

数的划分

将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。1,1,5; 1,5,1; 5,1,1;
问有多少种不同的分法。
输入:n,k ( 6 < n ≤ 200,2 ≤ k ≤ 6 ) 输出:一个整数,即不同的分法。

示例1

输入

//两个整数 n,k ( 6 < n ≤ 200, 2 ≤ k ≤ 6 )
7 3

输出

//1个整数,即不同的分法。
4

C++

法一:记忆化搜索
方法为减而治之,把n划分成k份的答案就相当于每次把n分成a,b两个数,再把a分成k-1份,然后把每次a分成k-1份的答案相加即可。注意点是每轮分出来的b要不大于上一轮分出来的b。

//https://www.nowcoder.com/questionTerminal/3773e51c48ec4727939cc85a8bc4c60d
#include <bits/stdc++.h>
using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define rFor(i,t,s) for (int i = (t); i >= (s); --i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)
#define rforeach(i,c) for (__typeof(c.rbegin()) i = c.rbegin(); i != c.rend(); ++i)

#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl

#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())

#define ms0(a) memset(a,0,sizeof(a))
#define msI(a) memset(a,inf,sizeof(a))

#define pii pair<int,int>
#define piii pair<pair<int,int>,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second

inline int gc(){
    static const int BUF = 1e7;
    static char buf[BUF], *bg = buf + BUF, *ed = bg;

    if(bg == ed) fread(bg = buf, 1, BUF, stdin);
    return *bg++;
} 

inline int ri(){
    int x = 0, f = 1, c = gc();
    for(; c<48||c>57; f = c=='-'?-1:f, c=gc());
    for(; c>47&&c<58; x = x*10 + c - 48, c=gc());
    return x*f;
}

typedef long long LL;
const int maxN = 1e5 + 7;

int n, k;
// f[i][j][k]表示数i分成j分的分法总数,k为限制条件,每种分法每份的值不能超过k,用来排除重复
// f[i][j][k] = f[i-1][j-1][1] + f[i-2][j-1][2] + ……+ f[i-min(k, i-1)][j-1][min(k, i-1)]
int f[201][7][202];

int solve(int x, int y, int z){
    int ret = 0;
    if(x < y) return 0;
    if(y == 1) return x <= z ? 1 : 0;
    if(f[x][y][z]) return f[x][y][z];

    For(i, 1, x-1) {
        if(x-i > z) continue;
        ret += solve(i, y-1, x-i);
    }
    f[x][y][z] = ret;
    return ret;
}

int main(){
    scanf("%d%d", &n, &k);
    printf("%d\n", solve(n, k, 201));
    return 0;
}

法二:DP

#include <bits/stdc++.h>
using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define rFor(i,t,s) for (int i = (t); i >= (s); --i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)
#define rforeach(i,c) for (__typeof(c.rbegin()) i = c.rbegin(); i != c.rend(); ++i)

#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl

#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())

#define ms0(a) memset(a,0,sizeof(a))
#define msI(a) memset(a,inf,sizeof(a))

#define pii pair<int,int>
#define piii pair<pair<int,int>,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second

inline int gc(){
    static const int BUF = 1e7;
    static char buf[BUF], *bg = buf + BUF, *ed = bg;

    if(bg == ed) fread(bg = buf, 1, BUF, stdin);
    return *bg++;
} 

inline int ri(){
    int x = 0, f = 1, c = gc();
    for(; c<48||c>57; f = c=='-'?-1:f, c=gc());
    for(; c>47&&c<58; x = x*10 + c - 48, c=gc());
    return x*f;
}

typedef long long LL;
const int maxN = 1e5 + 7;

int n, k;
// f[i][j]表示数i分成j份的分法总数
/*
    当i < j时,很明显没法分,所以f[i][j] = 0;
    当i == j时,只有一种分法,所以f[i][j] = 1;
    当i > j时,考虑从小到大分,第1个如果分1,那么f[i][j] = f[i-1][j-1];
  第1个如果分大于1的数,可以对所有j份都减一,然后再分,即 f[i][j] = f[i-j][j];
  根据加法原则,f[i][j] = f[i-1][j-1] + f[i-j][j];
*/
int f[201][7]; 

int main(){
    scanf("%d%d", &n, &k);
    For(i, 1, n) f[i][1] = 1; // 无论什么数,分成一份都只有一种
    For(i, 2, k)
        For(j, 2, n)
            if(j >= i) f[j][i] = f[j-1][i-1] + f[j-i][i];
    printf("%d\n", f[n][k]);
    return 0;
}

利用递归选数

已知 n 个整数 x1,x2,…,xn,以及一个整数 k(k<n)。从 n 个整数中任选 k 个整数相加,可分别得到一系列的和。例如当 n=4,k=3,4 个整数分别为 3,7,12,19 时,可得全部的组合与它们的和为:
3+7+12=22  3+7+19=29  7+12+19=38  3+12+19=34。
现在,要求你计算出和为素数共有多少种。 例如上例,只有一种的和为素数:3+7+19=29)。

示例

输入

//输入格式为:n , k(1<=n<=20,k<n) x1,x2,…,xn (1<=xi<=5000000)
4 3
3 7 12 19

输出

//输出格式为:一个整数(满足条件的种数)。
1

C

#include<stdio.h>
#include<math.h>

long long  A[22];

int n,k,ans;

int  Is_prime(long long  x);
void dfs(long long sum,int num,int left,int right);

int main(void)
{
    int i;
    scanf("%d%d",&n,&k);
    for( i = 1; i <=n ; i++ )
    {
        scanf("%d",&A[i]);
    }

    dfs(0,0,1,n);

//    printf("n = %d,k = %d\n",n,k);
//      for(i = 1; i <= n; i++)
//       {
//          printf("A[%d] = %d\n",i,A[i]);
//      }

    printf("%d\n",ans);

}

int  Is_prime(long long  x)
{
    int i;
    if(x==0||x==1)
        return 0;

    for( i=2;i<=sqrt(x);i++)
    {
        if(x%i==0)return 0;
    }

    return 1;
}

//      sum  已选数的和   ,num  已选个数 ,left,right  选择的区间
void dfs(long long sum,int num,int left,int right)
{

    if(num == k  && Is_prime(sum))                     //递归出口
        ans++;

//    printf("sum = %d,num = %d,left = %d,right = %d,ans = %d\n",sum,num,left,right,ans);

    if(num <= k - 1)
        for(int i = left;i <= right;i++)
        {
            dfs(sum + A[i],num+1,i + 1,right);
        }
}

C++

#include <bits/stdc++.h>
using namespace std;
#define ll long long

ll ans=0,n,k,vis[50],a[50],b[50];

inline void dfs_(ll x){
    if(x>k){
        ll sum=0;
        for(ll i=1;i<=k;i++) sum+=a[b[i]];
        ll bj=0;
        if(sum==1) bj=1;
        for(ll i=2;i<=sqrt(sum);i++){
            if( (sum%i)==0 ){
                bj=1;
                break;
            }
        }
        if(bj) return;
        else {
            ans++;
            return;
        }
    }

    for(ll i=b[x-1]+1;i<=n;i++){
        if(vis[i]) continue;
        b[x]=i;
        vis[i]=1;
        dfs_(x+1);
        vis[i]=0;
    }
}

inline void init_(){
    freopen("zuheshu.txt","r",stdin);
}

inline void readda_(){
    memset(b,0,sizeof(b));
    memset(vis,0,sizeof(vis));
    memset(a,0,sizeof(a));
    scanf("%lld%lld",&n,&k);
    for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
}

inline void work_(){
    dfs_(1);
    printf("%lld",ans);
}

int main(){
    init_();
    readda_();
    work_();
    return 0;
}

单词接龙

单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如beast和astonish,如果接成一条龙则变为beastonish,另外相邻的两部分不能存在包含关系,例如at和atide间不能相连。

示例1

输入

//输入的第一行为一个单独的整数n(n ≤ 20)表示单词数,以下n行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在.
5
at
touch
cheat
choose
tact
a

输出

//只需输出以此字母开头的最长的“龙”的长度
23

Java

import java.util.Scanner;

public class Main {

    static int n = 0, result = 0;
    static String[] word; // 记录字符串
    static char first; // 记录开头的字母
    static int visit[]; // 记录单词出现的次数
    static String link; // 记录连接串

    // dfs搜索
    static void dfs(String str) {
        String temp = str;
        if (result <= str.length()) {
            result = str.length();
        }
        for (int i = 0; i < word.length; i++) {
            if (visit[i] < 2 && connect(str, word[i])
                    && check(str, word[i]) == false) {
                visit[i]++;
                dfs(link);
                str = temp; // 一定要回溯!不要改变str!
                visit[i]--;
            }
        }
    }

    // 检查是否为最小重合部分
    static boolean connect(String a, String b) {
        char a1[] = a.toCharArray();
        char b1[] = b.toCharArray();
        for (int i = 0; i < Math.min(a.length(), b.length()); i++) {
            // 如果a1末尾的和a2开头的相等
            if (a1[a1.length - 1] == b1[i]) {
                // 两个串分别往前推着检查
                for (int j = a1.length - 1, k = i; j >= 0 && k >= 0; j--, k--) {
                    if (a1[j] != b1[k]) {
                        return false;
                    }
                    // 如果检查直到b1的第一位,都相等,则找到了重合部分
                    if (k == 0) {
                        b = b.substring(i + 1);// 取出这个重合部分,留下后面的字符串
                        link = a + b;// 形成连接串
                        return true;
                    }
                }
            }
        }
        return false;
    }

    // 检查是否有包含关系
    public static boolean check(String a, String b) {
        // 相同不算在包含的情况下
        if (a.equals(b)) {
            return false;
        }
        // 没有包含关系:
        for (int i = 0; i < Math.min(a.length(), b.length()); i++) {
            if (a.charAt(i) != b.charAt(i)) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        word = new String[n];
        for (int i = 0; i < n; i++) {
            word[i] = sc.next();
        }
        first = sc.next().charAt(0);
        visit = new int[n];
        sc.close();

        for (int i = 0; i < word.length; i++) {
            if (word[i].charAt(0) == first) {
                dfs(word[i]);
            }
        }
        System.out.println(result);
    }
}
//https://blog.csdn.net/sobermineded/article/details/79699222

C++

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
using namespace std;
int n;
string s1[25];
int vis[25];
int ans;
string s2;
bool check(string ss1,string ss2,int k)
{
    int l=ss1.length();
    for(int i=0;i<k;++i)
        if(ss1[l-k+i]!=ss2[i])return 0;
    return 1;
}
string add(string tmp,string s,int k)
{
    int l=s.length();
    for(int i=k;i<l;++i)
        tmp+=s[i];
    return tmp;
}
void dfs(string now)
{
    int len=now.length();
    ans=max(ans,len);
    for(int i=1;i<=n;++i)
    {
        if(vis[i]>=2)continue;
        int len2=s1[i].length();
        for(int j=1;j<len2;++j)
        {
            if(check(now,s1[i],j))
            {
                string tmp=now;
                tmp=add(tmp,s1[i],j);
                vis[i]++;
                dfs(tmp);
                vis[i]--;
            }
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)cin>>s1[i];
    cin>>s2;
    dfs(s2);
    printf("%d",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/wwj99/p/12230754.html

时间: 2024-11-02 01:57:16

数据结构与算法——前缀树和贪心算法(2)的相关文章

数据结构与算法——前缀树和贪心算法(1)

介绍前缀树 何为前缀树?如何生成前缀树? 例子:一个字符串类型的数组arrl,另一个字符串类型的数组arr2.arr2中有哪些字符,是arr 1中 出现的?请打印.arr2中有哪些字符,是作为arr 1中某个字符串前缀出现的?请打印.arr2 中有哪些字符,是作为arr1中某个字符串前缀出现的?请打印arr2中出现次数最大的前缀. public class TrieTree { public static class TrieNode { public int path; public int

算法导论——lec 13 贪心算法与图上算法

之前我们介绍了用动态规划的方法来解决一些最优化的问题.但对于有些最优化问题来说,用动态规划就是"高射炮打蚊子",采用一些更加简单有效的方法就可以解决.贪心算法就是其中之一.贪心算法是使所做的选择看起来是当前最佳的,期望通过所做的局部最优选择来产生一个全局最优解. 一. 活动选择问题 [问题]对几个互相竞争的活动进行调度:活动集合S = {a1, a2, ..., an},它们都要求以独占的方式使用某一公共资源(如教室),每个活动ai有一个开始时间si和结束时间fi ,且0 ≤ si &

Java 算法(一)贪心算法

Java 算法(一)贪心算法 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 一.贪心算法 什么是贪心算法?是指在对问题进行求解时,总是做出当前看来是最好的选择.也就是说,不从整体最优上加以考虑,所得出的结果仅仅是某种意义上的局部最优解. 因此贪心算法不会对所有问题都能得到整体最优解,但对于很多问题能产生整体最优解或整体最优解的近似解. 贪心算法的构成部分: 候选对象集合 :候选添加进解的对象的结合· 解对象集合 :初始时

疯子的算法总结(四)贪心算法

一.贪心算法 解决最优化问题的算法一般包含一系列的步骤,每一步都有若干的选择.对于很多最优化问题,只需要采用简单的贪心算法就可以解决,而不需要采用动态规划方法.贪心算法使所做的局部选择看起来都是当前最佳的,通过局部的最优化选择来产生全局最优解.本文将介绍贪心算法的理论基础和一些简单应用.在求最优解问题的过程中,依据某种贪心标准,从问题的初始状态出发,直接去求每一步的最优解,通过若干次的贪心选择,最终得出整个问题的最优解,这种求解方法就是贪心算法. 设计贪心算法的步骤: 将最优化问题转化为这样一个

python常用算法(6)——贪心算法,欧几里得算法

1,贪心算法 贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择.也就是说,不从整体最优上加以考虑,他所做出的的时在某种意义上的局部最优解. 贪心算法并不保证会得到最优解,但是在某些问题上贪心算法的解就是最优解.要会判断一个问题能否用贪心算法来计算.贪心算法和其他算法比较有明显的区别,动态规划每次都是综合所有问题的子问题的解得到当前的最优解(全局最优解),而不是贪心地选择:回溯法是尝试选择一条路,如果选择错了的话可以“反悔”,也就是回过头来重新选择其他的试试. 1.1

【算法复习】分治算法、动态规划、贪心算法

Notes ## 分治思想和递归表达式 [分治思想] 将一个问题分解为与原问题相似但规模更小的若干子问题,递归地解这些子问题,然后将这些子问题的解结合起来构成原问题的解.这种方法在每层递归上均包括三个步骤: divide(分解):将问题划分为若干个子问题 conquer(求解):递归地解这些子问题:若子问题Size足够小,则直接解决之 Combine(组合):将子问题的解组合成原问题的解 [分治递归表达式] 设T(n)是Size为n的执行时间,若Size足够小,如n ≤ C (常数),则直接求解

算法初级面试题07——前缀树应用、介绍和证明贪心策略、拼接字符串得到最低字典序、切金条问题、项目收益最大化问题、随时取中位数、宣讲会安排

第六课主要介绍图,不经常考,故今天先讲第七课的内容,介绍比较常考的树和贪心算法 介绍前缀树 何为前缀树? 如何生成前缀树? 可以查有多少个字符串以"be"为前缀. 如果要判断有没有"be"这个节点,每个节点上加上一个数据项,有多少个字符串以当前节点结尾的(可以查加了多少次特定字符串). 给一个字符串.返回多少个字符串以这个为前缀. 再加一个数据项,记录该节点被划过多少次. 大概实现: 删除逻辑: 根据path是否变为0,来判断是否继续往下删. 可以解决以下问题: 一

零基础学贪心算法

本文在写作过程中参考了大量资料,不能一一列举,还请见谅.贪心算法的定义:贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择.也就是说,不从整体最优上加以考虑,只做出在某种意义上的局部最优解.贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关.解题的一般步骤是:1.建立数学模型来描述问题:2.把求解的问题分成若干个子问题:3.对每一子问题求解,得到子问题的局部最优解:4.把子问题的局部最优

从零开始学贪心算法

本文在写作过程中参考了大量资料,不能一一列举,还请见谅. 贪心算法的定义: 贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择.也就是说,不从整体最优上加以考虑,只做出在某种意义上的局部最优解.贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关. 解题的一般步骤是: 1.建立数学模型来描述问题: 2.把求解的问题分成若干个子问题: 3.对每一子问题求解,得到子问题的局部最优解: 4.把子