LDU_软件工程 算法分析与设计(二)暴力搜索

 

问题 A: 0-1背包问题(基于暴力)

题目描述

给定一个容积为m的背包,去尝试装n个重量为wi、价值为vi的物体,求能装下的物体的最大价值。

输入

输入的第一行有两个整数n和m,分别表示物品的个数,背包的最大容量。 
接下来n行,每行两个数字,每个物品的重量w和价值v 
数据保证1<=n<=20,1<=w , v<=2000 

输出

一个整数,表示可获得的最大价值。

样例输入

3 30
16 45
15 25
15 25

样例输出

50

思想:裸的01背包

#include<bits/stdc++.h>
using namespace std;
const int maxn=40005;
int dp[maxn];
 
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
    {
        int w,v;
        scanf("%d%d",&w,&v);
        for(int j=m;j>=w;j--)
            dp[j]=max(dp[j],dp[j-w]+v); 
    }
    int ans=0;
    for(int i=0;i<=m;i++)
        ans=max(ans,dp[i]);
    printf("%d\n",ans);
    return 0;
} 

问题 B: 八皇后问题(基于暴力)

题目描述

皇后问题。所谓的n皇后问题,是指在n×n的棋盘上放置彼此不受攻击的n个皇后,按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。后问题等价于在n×n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。 
为了使问题更加有趣,我们需要求在n×n的棋盘上,放置k个皇后,共有多少种可能的方案数? 

输入

两个正整数n和k,(n,k<=10)

输出

一个整数,表示方案数。

样例输入

8 8

样例输出

92

思想:n*n的棋盘放k个皇后,dfs暴力加剪枝。当然可以本地打表,输出表。

#include<bits/stdc++.h>
using namespace std;
int sum,n,k;
int Map[15];
int check(int x)
{
    for(int i=1;i<x;i++)
        if((abs(x-i)==abs(Map[x]-Map[i])) || (Map[x]==Map[i]))
            return 0;
    return 1;
}
void dfs(int id,int flag)
{
    if(id>n)
    {
        if(flag==0)
            sum++;
        return ;
    }
    else if(n-id+1 < flag)
        return ;
    for(int i=1;i<=n;i++)
    {
        Map[id]=i;
        if(check(id))
            dfs(id+1,flag-1);
    }   
    Map[id]=-1000;
    dfs(id+1,flag);
}
int main()
{
    scanf("%d%d",&n,&k);
    sum=0;
    if(n>=k)
        dfs(1,k); 
    printf("%d\n",sum);
    return 0;
} 

问题 C: 李白饮酒--蓝桥杯原题改编(基于暴力)

题目描述

话说大诗人李白,一生好饮。幸好他从不开车。一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:

无事街上走,提壶去打酒。逢店加一倍,遇花喝一斗。 

这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能方案的个数。 

为了使问题更加有趣,我们假设他遇到店s次,花f次,你的任务是计算此时的方案总数。 

输入

两个整数s和f,分别表示李白遇到的店和花的次数。(s+f<=20)

输出

一个整数,表示方案总数。

样例输入

5 10

样例输出

14

思想:2进制枚举,最多2^10  

#include<bits/stdc++.h>
using namespace std;
int ans,s,f;
void dfs(int dian,int hua,int jiu,int flag)
{
    if(dian==s && hua==f && jiu==0 && flag==1)
    {
        ans++;
        return ;
    }
    if(dian<s)
        dfs(dian+1,hua,jiu*2,0);
    if(hua<f)
        dfs(dian,hua+1,jiu-1,1);
}
int main()
{
    scanf("%d%d",&s,&f);
    if(f==0)
    {
        printf("0\n");
        return 0;
    }
    ans=0;
    dfs(0,0,2,0);
    printf("%d\n",ans);
    return 0;
}

问题 D: 组素数

题目描述

素数就是不能再进行等分的数。比如:2、3、5、7、11 等。9 = 3 * 3 说明它可以3等分,因而不是素数。

我们国家在1949年建国。如果只给你 1、9、4和9 这4个数字卡片,可以随意摆放它们的先后顺序,那么,你能组成多少个4位的素数呢? 

比如:1949,4919 都符合要求。 

为了使问题更加有趣,我们输入n个数字,求这n个数字可以组成的数字中的素数。 

输入

第一行一个整数n。(n<=6)
第二行n个空格分隔的整数,仅包含1~9 

输出

输出所有符合的素数。若没有可行解,则输出-1。

样例输入

4
1 9 4 9

样例输出

1499
1949
4919
9419
9491
9941

思想:1e6素数打表,然后全排列一下判断素数就行了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int a[10];
int vis[maxn];
void init()
{
    for(int i=4;i<maxn;i+=2)
        vis[i]=1;
    for(int i=3;i<maxn;i++)
    {
        if(vis[i]==0)
        {
            for(int j=i+i;j<maxn;j+=i)
                vis[j]=1;
        }
    }       
}
int main()
{
    init();
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    sort(a,a+n);
    int flag=0;
    int ans=0;
    do{
        int Ans=0;
        for(int i=0;i<n;i++)
            Ans=Ans*10+a[i];
        if(vis[Ans]==0)
        {
            flag=1;
            printf("%d\n",Ans);
        }
    }while(next_permutation(a,a+n));
    if(flag==0)
        printf("-1\n");
    return 0;
}

问题 E: 匪警110问题

题目描述

匪警请拨110,即使手机欠费也可拨通!为了保障社会秩序,保护人民群众生命财产安全,警察叔叔需要与罪犯斗智斗勇,因而需要经常性地进行体力训练和智力训练! 
某批警察叔叔正在进行智力训练: 
12 3 4 5 6 7 8 9 = 110 
    请看上边的算式,为了使等式成立,需要在数字间填入加号或者减号(可以不填,但不能填入其它符号)。之间没有填入符号的数字组合成一个数,例如:12+34+56+7-8+9 就是一种合格的填法;123+4+5+67-89 是另一个可能的答案。 
    请你利用计算机的优势,帮助警察叔叔快速找到所有答案。形如: 
12+34+56+7-8+9 
123+4+5+67-89 
.... 
为了使问题更加有趣,我们把后面的数字110换成n。注意:这里只要求你计算方案数。不必输出每种方案。 

输入
一个整数n,如题所述。(-2000000000<=n<=2000000000) 

输出

一个整数,表示方案数。

样例输入

110

样例输出

10

思想:三进制枚举下,考虑当前放什么。3^8

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int a[10]={1,2,3,4,5,6,7,8,9};
    int ans=0;
    int n;
    scanf("%d",&n);
    int m=(int)pow(3,8);
    for(int i=0;i<m;i++)
    {
        int TT=1,sum=0;
        int temp=i;
        int op=1;
        for(int j=1;j<9;j++)
        {
            if(temp%3==0)
                TT=TT*10+a[j];
            else if(temp%3==1)
            {
                if(op==1)
                {
                    sum+=TT;
                    TT=a[j]; 
                }
                else
                {
                    sum-=TT;
                    TT=a[j];
                }
                op=1;   
            }
            else
            {
                if(op==1)
                {
                    sum+=TT;
                    TT=a[j]; 
                }
                else
                {
                    sum-=TT;
                    TT=a[j];
                }
                op=2;   
            }
            temp/=3;    
        }
        if(op==1)
            sum+=TT;
        else
            sum-=TT;
        if(sum==n)
            ans++; 
    }
    printf("%d\n",ans);
    return 0;
}

问题 F: 敢死队

题目描述

G将军有一支训练有素的军队,这个军队除了G将军外,每名士兵都有一个直接上级(可能是其他士兵,也可能是G将军)。现在G将军将接受一个特别的任务,需要派遣一部分士兵(至少一个)组成一个敢死队,为了增加敢死队队员的独立性,要求如果一名士兵在敢死队中,他的直接上级不能在敢死队中。请问,G将军有多少种派出敢死队的方法。注意,G将军也可以作为一个士兵进入敢死队。 

输入

   输入的第一行包含一个整数n(1<=n<=1000),表示包括G将军在内的军队的人数。军队的士兵从1至n编号,G将军编号为1。接下来n-1个数,分别表示编号为2, 3, ..., n的士兵的直接上级编号,编号i的士兵的直接上级的编号小于i。

输出

一个整数,表示方法数。注意:答案可能会很大,可能不足以64位整数存储,因此你需要输出模除1000000007后的结果。

样例输入

4
1 2 3

样例输出

7

思想:树形DP,考虑子孩子对于父亲节点的影响,如果当前不去,那么他的子孩子都可以,如果当前去,他的子孩子都不能去。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
typedef long long ll;
const ll mod = 1e9+7; 
ll dp[maxn][2];
vector<int>V[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        int fa;
        scanf("%d",&fa);
        V[fa].push_back(i);
    } 
    for(int i=n;i>=1;i--)
    {
        dp[i][0]=dp[i][1]=1ll;
        for(int j=0;j<V[i].size();j++)
        {
            //printf("~%lld %lld\n",dp[V[i][j]][0],dp[V[i][j]][1]);
            dp[i][0]=(dp[i][0]%mod*(dp[V[i][j]][0]+dp[V[i][j]][1])%mod)%mod;
            dp[i][1]=(dp[i][1]%mod*dp[V[i][j]][0]%mod)%mod;
            //printf("%lld %lld\n",dp[i][0],dp[i][1]);  
        }
    }
    printf("%lld\n",(dp[1][0]+dp[1][1]-1ll)%mod);
    return 0;
}

问题 G: 独立任务最优调度问题

题目描述

用两台处理机A和B处理n个作业。设第i个作业交给A处理需要时间ai,交给B处理需要时间bi。由于各作业的特点和机器的性能关系,ai和bi之间没有明确的大小关系。既不有将一个作业分开由2台机器处理,也没有一台机器能同时处理2个作业。设计一个算法,使得这两台机器处理完这n个作业的时间最短。

输入

第一行一个整数n,表示任务数(1<=n<=20)。 
第二行n个整数,分别表示第i个任务在机器A上的处理时间。 
第三行n个整数,分别表示第i个任务在机器B上的处理时间。

处理时间数据范围(1~40) 

输出

一个整数,表示最少的完成这些任务的时间 。

样例输入

3
1 4 7
2 5 8

样例输出

7

思想:直接2进制枚举情况取一个最小的即可。2^20 。

或者可以考虑下DP     dp[i][j]表示第i个任务,A机器花费了j时间。dp[i][j]=min(dp[i-1][j]+b[i],dp[i-1][j-a[i])

枚举:

#include<bits/stdc++.h>
using namespace std;
int a[25];
int b[25];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    for(int i=0;i<n;i++)
        scanf("%d",&b[i]);
    int m=(int)pow(2,n);
    int ans=1<<30;
    for(int i=0;i<=m;i++)
    {
        int temp=i;
        int sum1=0;
        int sum2=0;
        for(int j=0;j<n;j++)
        {
            if(temp%2==0)
                sum1+=a[j];
            else
                sum2+=b[j];
            temp=temp/2;
        }
        temp=max(sum1,sum2);
        ans=min(ans,temp);
    }
    printf("%d\n",ans);
    return 0;
}

DP

#include<bits/stdc++.h>
using namespace std;
int a[25];
int b[25];
int dp[25][1000];
int main()
{
    int n,sum=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
        sum+=a[i];
    }
    for(int i=0;i<n;i++)
        scanf("%d",&b[i]);
     
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=sum;j++)
        {
            if(j<a[i-1])//A不够做这个任务 
                dp[i][j] = dp[i-1][j]+b[i-1];
            else if(dp[i-1][j]+b[i-1]<dp[i-1][j-a[i-1]])
                dp[i][j] = dp[i-1][j]+b[i-1];
            else
                dp[i][j] = dp[i-1][j-a[i-1]];       
        }       
    }
    int Max=1<<30;
    for(int i=0;i<=sum;i++)
        Max=min(Max,(max(dp[n][i],i)));
    printf("%d\n",Max);
    return 0;
} 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
专题十:算法分析设计 1.常用的算法设计方法:   1.1 迭代法   1.2 穷举搜索法   1.3 递推法   1.4 递归法   1.5 贪婪法   1.6 分治法   1.7 动态规划法   1.8 回溯法 算法基础部分: 算法是对特定问题求解步骤的一种描述,算法是指令的有限序列,其中每一条指令表示一个或多个操作。 算法具有以下5个属性:   有穷性:一个算法必须总是在执行有穷步之后结束,且每一步都在有穷时间内完成。   确定性:算法中每一条指令必须有确切的含义。不存在义性。只有一个入口和一个出口   可行性:一个算法是可行的就是算法描述的操作是可以通过已经实现的基本运算执行有限次来实现的。   输入:一个算法有零个或多个输入,这些输入取自于某个特定对象的集合。   输出:一个算法一个或多个输出,这些输出同输入有着某些特定关系的量。 所以对应的算法设计的要求:   正确性:算法应满足具体问题的需求;   可读性:算法应该好读,以有利于读者对程序的理解;   健壮性:算法应具有容错处理,当输入为非法数据时,算法应对其作出反应,而不是产生莫名其妙的输出结果。   效率与存储量需求:效率指的是算法执行的时间;存储量需求指算法执行过程中所需要的最大存储空间。一般这两者与问题的规模有关。 1.1 迭代法: 迭代法是用于求方程或方程组近似根的一种常用的算法设计方法。设方程为f(x)=0,用某种数学方法导出等价的形式x=g(x),然后按以下步骤执行: (1)选一个方程的近似根,赋给变量x0; (2)将x0的值保存于变量x1,然后计算g(x1),并将结果存于变量x0; (3)当x0与x1的差的绝对值还小于指定的精度要求时,重复步骤(2)的计算。 若方程有根,并且用上述方法计算出来的近似根序列收敛,则按上述方法求得的x0就认为是方程的根。上述算法用C程序的形式表示为: 【算法】迭代法求方程的根 { x0=初始近似根; do { x1=x0; x0=g(x1); /*按特定的方程计算新的近似根*/ } while ( fabs(x0-x1)>Epsilon); printf(“方程的近似根是%f\n”,x0); } 迭代算法也常用于求方程组的根,令 X=(x0,x1,…,xn-1) 设方程组为: xi=gi(X) (I=0,1,…,n-1) 则求方程组根的迭代算法可描述如下: 【算法】迭代法求方程组的根 { for (i=0;i<n;i++) x[i]=初始近似根; do { for (i=0;i<n;i++) y[i]=x[i]; for (i=0;i<n;i++) x[i]=gi(X); for (delta=0.0,i=0;i<n;i++) if (fabs(y[i]-x[i])>delta) delta=fabs(y[i]-x[i]); } while (delta>Epsilon); for (i=0;i<n;i++) printf(“变量x[%d]的近似根是 %f”,I,x[i]); printf(“\n”); } 具体使用迭代法求根时应注意以下两种可能发生的情况: (1)如果方程无解,算法求出的近似根序列就不会收敛,迭代过程会变成死循环,因此在使用迭代算法前应先考察方程是否有解,并在程序中对迭代的次数给予限制; (2)方程虽然有解,但迭代公式选择不当,或迭代的初始近似根选择不合理,也会导致迭代失败。 1.2 穷举搜索法: 穷举搜索法是对可能是解的众多候选解按某种顺序进行逐一枚举和检验,并从中找出那些符合要求的候选解作为问题的解。 要解决的问题只有有限种可能,在没有更好算法时总可以用穷举搜索的办法解决,即逐个的检查所有可能的情况。可以想象,情况较多时这种方法极为费时。实际上并不需要机械的检查每一种情况,常常是可以提前判断出某些情况不可能取到最优解,从而可以提前舍弃这些情况。这样也是隐含的检查了所有可能的情况,既减少了搜索量,又保证了不漏掉最优解。 【问题】 将A、B、C、D、E、F这六个变量排成如图所示的三角形,这六个变量分别取[1,6]上的整数,且均不相同。求使三角形三条边上的变量之和相等的全部解。如图就是一个解。 程序引入变量a、b、c、d、e、f,并让它们分别顺序取1至6的整数,在它们互不相同的条件下,测试由它们排成的如图所示的三角形三条边上的变量之和是否相等,如相等即为一种满足要求的排列,把它们输出。当这些变量取尽所有的组合后,程序就可得到全部可能的解。细节见下面的程序。 # include <stdio.h> void main() { int a,b,c,d,e,f; for (a=1;a<=6;a++) //a,b,c,d,e依次取不同的值 for (b=1;b<=6;b++) { if (b==a) continue; for (c=1;c<=6;c++) { if (c==a)||(c==b) continue; for (d=1;d<=6;d++) { if (d==a)||(d==b)||(d==c) continue; for (e=1;e<=6;e++) { if (e==a)||(e==b)||(e==c)||(e==d) continue; f=21-(a+b+c+d+e);//最后一个用减法算 if ((a+b+c==c+d+e))&&(a+b+c==e+f+a)) { printf(“%6d,a); printf(“%4d%4d”,b,f); printf(“%2d%4d%4d”,c,d,e); scanf(“%c”); } } } } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值