2024-2025《算法设计与分析》课程作业习题(期末复习版)

填空题

4-1

求整数n(n≥0)的阶乘的算法代码如下,其时间复杂度是 O (n)。

int fact(int n){

if(n <= 1) return 1;

return n * fact(n - 1);

}

4-2

函数2logn 3 的渐进表达式是O(logn)。

4-3

函数1314+ 1/n 的渐进表达式是O(1)。

4-4

时间复杂度为O(nlogn)的排序算法有归并排序、堆排序和(快速)排序。

4-5

由权值分别为(3,8,6,2,5)的叶子结点生成一棵哈夫曼树,它的带权路径长度为(53)。

函数题

6-1 旅途加油

题目

一辆汽车加满油后可行驶nkm。旅途中有若干加油站。设计一个有效算法,对于给定的n和k个加油站位置,计算最少加油次数。

输入格式:
第1行输入两个整数n和k,分别表示表示汽车加满油后可行驶nkm,旅途中有k个加油站
第2行输入k+1个整数,表示第k个加油站与第k-1个加油站之间的距离。第0个加油站表示出发地,汽车已加满油。第k+1个加油站表示目的地。

输出格式:
输出最少加油次数。如果无法到达目的地,则输出“No Solution!”。
函数接口定义:

int Fill(int stations[],int n,int k);

其中, stations是用户传入的k+1个相邻加油站的距离,n是油箱满的时候可以走的距离,k是中间加油站的数量;函数须返回 最少加油次数,如果无法到达目的地,函数返回0。
裁判测试程序样例:

int Fill(int stations[],int n,int k);
int main(){
    int stations[1000];
    int n; //油箱满的时候可以走的距离
    int k;//中间加油站的数量
    int i,all;
    scanf("%d%d",&n,&k);
    for(i=0;i<k+1;i++)
    scanf("%d",&stations[i]);
    all=Fill(stations,n,k);
    if(all!=0)
    printf("%d\n",all);//输出总的加油次数
    else
    printf("No Solution!");

}
/* 请在这里填写答案 */

输入样例:
在这里给出一组输入。例如:

7 7
1 2 3 4 5 1 6 6

输出样例:
在这里给出相应的输出。例如:

4

答案:

//C语言
int Fill(int stations[],int n,int k){
    
    
    //先用贪心算法做一遍
    int ans=0;//用来记录已加油次数
    int remain=n;//用来表示还剩余的油量
    int flag=1;//表示最近一站是否加油,0代表没有,1代表有
    for(int i=0;i<k+1;i++){
    
    
        if(stations[i]<=remain){
    
    //当目前油量足以到达下一站时,不加油
            remain-=stations[i];
            flag=0;
        }else if(flag==0){
    
    //当目前油量不足以达到下一站时,且该站还未加过油时
            flag=1;
            ans+=1;
            remain=n;
            i--;//加满油后重新判断
        }else{
    
    //当前站已经加过油,但还是不足以到达下一站
            return 0;
        }
    }
    return ans;
}

6-2杨辉三角-递归

题目

输入一个整数n(n<=10),输出n行构成的杨辉三角,要求设计递归函数实现。
函数接口定义:

int Yang(int i, int j);

参数i和j代表最终三角形的第i行第j列,函数的返回值则是杨辉三角第i行第j列上的正确数值。用递归实现。
裁判测试程序样例:

int Yang(int i, int j);
int main()
{
    
    
    int n, i, j;
    scanf("%d",&n);
    for(i = 1; i <= n; i++)
    {
    
    
        for(j = 1; j <= i; j++)
        {
    
    
            printf("%4d", Yang(i,j));
        }
        printf("\n");
    }
    return 0;
}

/* 请在这里填写答案 */

输入样例:
一个正整数

4

输出样例:
每个数占4列

   1
   1   1
   1   2   1
   1   3   3   1


答案

int Yang(int i,int j){
    
    
    //杨辉三角的特性 当j=1或i=j时,都为1
    if(j==1||i==j){
    
    
        return 1;
    }
    return Yang(i-1,j-1)+Yang(i-1,j);
}

6-3求解编辑距离问题(动态规划法)

题目

设A和B是两个字符串。现在要用最少的字符操作次数,将字符串A转换为字符串B。这里所说的字符操作共有3种:
(1)删除一个字符。
(2)插入一个字符。
(3)将一个字符替换另一个字符。
函数接口定义:

void solve();

裁判测试程序样例:

#include<stdio.h>
#include<string>
#include<iostream>
#include<algorithm> 
using namespace std;
#define MAX 201
//问题表示
string a;
string b;
//求解结果表示
int dp[MAX][MAX];
void solve();                //求dp

int main()
{
    
        cin>>a>>b;
    solve();
    int i,j;
  printf("%d",dp[a.length()][b.length()]);
    return 0;
}

/* 请在这里填写答案 */

输入格式:
第一行输入A字符串,第二行输入B字符串。

输出格式:
输出最少的字符操作次数。

输入样例1:

sfdqxbw
gfdgw

输出样例1:

4

答案

//c++
//动态规划
//dp[i][j]表示将字符串a的前i个字符转化为字符串b的前j个字符所需的最少操作次数
void solve(){
    
    
    //初始化
    dp[0][0]=0;
    dp[0][1]=1;
    dp[1][0]=1;
    for(int i=1;i<=a.length();i++){
    
    
        for(int j=1;j<=b.length();j++){
    
    
            if(a[i-1]==b[j-1]){
    
    //如果字符相等,则dp保持
                dp[i][j]=dp[i-1][j-1];
            }else{
    
    
                dp[i][j]=min(dp[i-1][j],dp[i][j-1])+1;//如果字符不相等,则dp延申
            }
        }
    }
}

6-4 电影安排

题目

终于到周末了,明明是特别喜欢看电影。他想在一天内尽量多的看到完整的多部电影。 现在他把他喜欢的电影的播放时间表给你,希望你能帮他合理安排。

输入格式:
输入的第一行是一个整数n(n<=100),表示明明喜欢的电影的总数。 接下来n行,每行输入两个整数si和ei(1<=i<=n),表示第i个电影的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。

输出格式:
输出能完整看到的电影的个数。

函数接口定义:

int Schedule(TIME a[],int n);//根据n部电影在一天的放映时间,返回至多能安排的电影场数
int cmp(TIME *a,TIME *b);//按结束时间升序排序的qsort排序规则,此处不表

快速排序库函数qsort原型为:void qsort ( void * base, size_t num, size_t size, int ( * comparator ) ( const void *, const void * ) ); 例如:qsort(a,n,sizeof(TIME),cmp);表示将数组a前n个元素按照cmp排序规则进行排序

裁判测试程序样例:

#include<bits/stdc++.h>
using namespace std;
typedef struct
{
    
    
int  start_time;
int  end_time;
}TIME;
int Schedule(TIME a[],int n);//根据n部电影在一天的放映时间,返回至多能安排的电影场数
bool cmp(const TIME &a,const TIME &b);//按结束时间升序排序的qsort排序规则,此处不表
int main()
{
    
    TIME  a[100];
 int n,i,k;
 scanf("%d",&n);
 for(i=0;i<n;i++)
scanf("%d%d",&a[i].start_time,&a[i].end_time);
k= Schedule(a,n);
printf("%d\n",k);
 }
bool cmp(const TIME &a,const TIME &b)  //按结束时间递增
{
    
    
    return a.end_time<b.end_time;
}
/* 请在这里填写答案 */

输入样例:
第一行输入一个整数n,表示有n场电影,随后n行,每行输入一场电影的开始时间和结束时间,中间用空格分隔。

12
1 3
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
5 10
4 14
2 9

输出样例:
输出最多能看的电影场数

5

答案

//C++
//用贪心算法求解
int Schedule(TIME a[],int n){
    
    
    sort(a,a+n,cmp);//先将数组按照结束时间升序排序
    int count=0;//用来记录看电影场数
    int myTime=0;//表示当前时间
    for(int i=0;i<n;i++){
    
    
        if(myTime<=a[i].start_time){
    
    
            count++;
            myTime=a[i].end_time;
        }
    }
    return count;
}

6-5 循环日程安排问题(分治法)

题目

用分治法求解循环日程安排问题。设有n=2^k个选手要进行网球循环赛,要求设计一个满足以下要求的比赛日程表:
(1)每个选手必须与其他n-1个选手各赛一次。
(2)每个选手一天只能赛一次。
(3)循环赛在n-1天之内结束。
在这里插入图片描述
函数接口定义:

void Plan(int a[][N],int k);

裁判测试程序样例:

#include <iostream>
#include <math.h>
#include <iomanip>
using namespace std;
#define N 100

void Plan(int a[][N],int k);

int main()
{
    
     int i,j,a[N][N],k,size;
cin>>k;
size=pow(2,k);
Plan(a,k);
for(i=1;i<=size;i++){
    
    
  for(j=1;j<=size;j++)
  {
    
    
    cout<<a[i][j]<<" ";
  }
  cout<<endl;
}
 return 0;
    
}

/* 请在这里填写答案 */

输入样例:
输入K值。

3

输出样例:
输出比赛日程表。

1 2 3 4 5 6 7 8 
2 1 4 3 6 5 8 7 
3 4 1 2 7 8 5 6 
4 3 2 1 8 7 6 5 
5 6 7 8 1 2 3 4 
6 5 8 7 2 1 4 3 
7 8 5 6 3 4 1 2 
8 7 6 5 4 3 2 1 

答案

//C++
//分治法 1分治 2递归 3合并
void Plan(int a[][N],int k){
    
    
    //递归出口
    if(k==1){
    
    
        a[1][1]=1;a[1][2]=2;
        a[2][1]=2;a[2][2]=1;
    }else{
    
    
        Plan(a,k-1);
        //将新行格子赋值
        int start=pow(2,k-1)+1;
        int end=pow(2,k);
        int t=end-start+1;
        for(int i=start;i<=end;i++){
    
    
            for(int j=1;j<=t;j++){
    
    
                a[i][j]=a[i-t][j]+t;
            }
        }
        //将对角线赋值
        for(int i=1;i<=end;i++){
    
    
            for(int j=start;j<=end;j++){
    
    
                if(i+t>end){
    
    //交换左上右下
                    a[i][j]=a[(i+t)%end][j-t];
                }else{
    
    //交换左下右上
                    a[i][j]=a[i+t][j-t];
                }
            }
        }
    }
}

6-6 归并排序

题目

归并排序算法,实现其中的mergeSort和merge两个函数。

函数接口定义:

void mergeSort(vector<int> &Array, int start, int end);
void merge(vector<int> &Array, int start, int mid, int end);

函数mergeSort完成归并排序(从小到大),其中 Array 是需要排序的数组,start是Array 中排序部分的起始位置,end是Array 中排序部分的结束位置。

函数merge完成数组中两个相邻有序部分(从小到大)的合并,合并后元素仍然有序(从小到大),其中 Array 是需要排序的数组,start是第一个有序部分的起始位置,mid是第一个有序部分的结束位置,第二个有序部分的起始位置为mid+1,end是第二个有序部分的结束位置。

裁判测试程序样例:

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

void mergeSort(vector<int> &Array, int start, int end);
void merge(vector<int> &Array, int start, int mid, int end);

int main(){
    
    
    
    int n;
    vector<int> a;
    vector<int>::iterator i;
    
    //输入数组
    cin >> n;
    a.resize(n);
    for(i = a.begin(); i != a.end(); i++){
    
    
        cin >> *i;
    }
    
    //归并排序
    mergeSort(a, 0, a.size()-1);
    
    //输出数组
    i = a.begin();
    cout << *i++;
    for( ; i != a.end(); i++){
    
    
        cout << " " << *i;
    }

    return 0;
}


/* 此处将 放置 提交的函数代码 */

输入样例:
第一行为数组元素个数

第二行为空格间隔的各个数组元素

5
3 1 2 5 4

输出样例:
输出排序后的数组元素

1 2 3 4 5

答案

//归并排序
void mergeSort(vector<int> &Array,int start,int end){
    
    
    //递归终止条件
    if(end<=start){
    
    
        return;
    }
    //分治
    int mid=(start+end)/2;
    mergeSort(Array,start,mid);
    mergeSort(Array,mid+1,end);
    //合并
    merge(Array,start,mid,end);
}
//合并
void merge(vector<int> &Array,int start,int mid,int end){
    
    
    int temp[end-start+1];
    int i=start,j=mid+1,k=0;
    while(i<=mid&&j<=end){
    
    
        if(Array[i]<=Array[j]){
    
    
            temp[k]=Array[i];
            i++;
        }else{
    
    
            temp[k]=Array[j];
            j++;
        }
        k++;
    }
    while(i<=mid){
    
    
        temp[k]=Array[i];
        k++;i++;
    }
    while(j<=end){
    
    
        temp[k]=Array[j];
        k++;j++;
    }
    k=0;
    for(i=start;i<=end;i++,k++){
    
    
        Array[i]=temp[k];
    }
}

6-7 哈夫曼树及编码

题目

构造哈夫曼树,计算WPL。

函数接口定义:

void CreateHTree();
void CreateHCode();

裁判测试程序样例:

#include <iostream>
#include <queue>
#include <vector>
#include <string>
#include <map>
using namespace std; 
#define MAX 101
int n;
struct HTreeNode                //哈夫曼树结点类型
{
    
    
    char data;                    //字符
    int weight;                    //权值
    int parent;                    //双亲的位置
    int lchild;                    //左孩子的位置
    int rchild;                    //右孩子的位置
};
HTreeNode ht[MAX];                //哈夫曼树
map<char,string> htcode;            //哈夫曼编码

struct NodeType        //优先队列结点类型
{
    
    
    int no;                //对应哈夫曼树ht中的位置
    char data;            //字符
    int  weight;        //权值
    bool operator<(const NodeType &s) const
    {
    
                        //用于创建小根堆
        return s.weight<weight;
    }
};
void CreateHTree();
void CreateHCode();

int WPL()                //求WPL
{
    
    
    int wps=0;
    for (int i=0;i<n;i++)
        wps+=ht[i].weight*htcode[ht[i].data].size();
    return wps;
}

int main()
{
    
    
    cin >> n;
    for(int i=0;i<n;i++)
    {
    
    
        cin >> ht[i].data >> ht[i].weight;
    }  
    CreateHTree();                    //建立哈夫曼树
    CreateHCode();                    //求哈夫曼编码
    printf("WPL=%d",WPL());
    return 0;
}
/* 请在这里填写答案 */

输入样例:
第一行输入一个数n(1<n<100),表示叶子节点的个数,接下去输入n行,每行输入一个字符和一个整数,表示每个节点表示的字符和权值。

5
A 8
B 10
C 2
D 11
E 1

输出样例:
输出WPL。

WPL=67

答案

//c++
//构造哈夫曼树
void CreateHTree(){
    
    
    //先按照权值排序
    priority_queue<NodeType> pq;
    for(int i=0;i<n;i++){
    
    
        //装载进优先队列
        NodeType node;
        node.no=i;
        node.data=ht[i].data;
        node.weight=ht[i].weight;
        pq.push(node);
        //给ht数组赋初值
        ht[i].parent=-1;
        ht[i].lchild=-1;
        ht[i].rchild=-1;
    }
    //构造哈夫曼树
    int pos=n;
    //新增n-1个中间节点
    for(int i=0;i<n-1;i++){
    
    
        //构造新中间节点
        NodeType left=pq.top();pq.pop();
        NodeType right=pq.top();pq.pop();
        ht[pos].weight=left.weight+right.weight;
        ht[pos].lchild=left.no;
        ht[pos].rchild=right.no;
        ht[pos].data='#';
        ht[pos].parent=-1;
        ht[left.no].parent=pos;
        ht[right.no].parent=pos;
        //加入优先队列
        NodeType node;
        node.no=pos;
        node.data='#';
        node.weight=ht[pos].weight;
        pq.push(node);
        pos++;
    }
}
//求哈夫曼编码
void CreateHCode(){
    
    
     for(int i=0;i<n;i++){
    
    
         string code="";
         int temp=i;
         while(ht[temp].parent!=-1){
    
    
             if(ht[ht[temp].parent].lchild==temp){
    
    
                 code+="0";
             }else{
    
    
                 code+="1";
             }
             temp=ht[temp].parent;
         }
         htcode[ht[i].data]=code;
     }
}

6-8 求解图的m着色问题(回溯法)

题目

给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色。如果有一种着色法使G中每条边的两个顶点着不同颜色,则称这个图是m可着色的。图的m着色问题是对于给定图G和m种颜色,找出所有不同的着色法。

函数接口定义:

void dfs(int i);

裁判测试程序样例:

#include <stdio.h>
#include <string.h>
#define MAXN 20                //图最多的顶点个数
int n,k,m;
int a[MAXN][MAXN];
int count=0;                //全局变量,累计解个数
int x[MAXN];                //全局变量,x[i]表示顶点i的着色

bool Same(int i)            //判断顶点i是否与相邻顶点存在相同的着色
{
    
    
    for (int j=1;j<=n;j++)
        if (a[i][j]==1 && x[i]==x[j])
            return false;
    return true;
}

int main()
{
    
    
    memset(a,0,sizeof(a));        //a初始化
    memset(x,0,sizeof(x));        //x初始化
    int x,y;
    scanf("%d%d%d",&n,&k,&m);    //输入n,k,m
    for (int j=1;j<=k;j++)
    {
    
    
        scanf("%d%d",&x,&y);    //输入一条边的两个顶点
        a[x][y]=1;                //无向图的边对称
        a[y][x]=1;
    }
    dfs(1);                        //从顶点1开始搜索
    if (count>0)                //输出结果
        printf("%d",count);
    else
        printf("-1");
    return 0;
}
/* 请在这里填写答案 */

输出格式:
程序运行结束时,将计算出的不同的着色方案数输出。如果不能着色,程序输出-1。

输入样例:

5 8 4
1 2
1 3
1 4
2 3
2 4
2 5
3 4
4 5

输出样例:

48

答案

//c++
//求解(i代表顶点)
void dfs(int i){
    
    
    //终止条件(收集答案)
    if(i>n){
    
    
        count++;
        return;
    }
    //遍历尝试着色
    for(int color=1;color<=m;color++){
    
    
        x[i]=color;
        if(Same(i)){
    
    
            dfs(i+1);
        }
        //回溯
        x[i]=0;
    }
    
}

6-9 最短路径(队列式分枝限界法)

题目

试实现最短路径算法。

函数接口定义:

void bfs();
void dispallpath();

裁判测试程序样例:

#include <stdio.h>
#include <string.h>
#include <queue>
#include <iostream>
using namespace std;
#define INF 0x3f3f3f3f            //表示∞
#define MAXN 51
int n,m;                        //图顶点个数和边数 
int a[MAXN][MAXN];                //图的邻接矩阵
int v;                            //源点
int dist[MAXN];                    //dist[i]源点到顶点i的最短路径长度
struct NodeType                    //队列结点类型
{
    
    
    int vno;                    //顶点编号
    int length;                    //路径长度
};

void bfs();
void dispallpath();

int main()
{
    
    
    memset(dist,INF,sizeof(dist));    //初始化为∞
    memset(a,INF,sizeof(a));        //初始化为∞
     cin >> n>> m; 
    for (int i=0;i<n;i++)            //对角线设置为0
        a[i][i]=0;
    for(int i=0;i<m;i++){
    
    
        int x,y,w;
        cin>>x>>y>>w;
        a[x][y]=w;
    } 
    cin >>v;
    bfs();
    dispallpath();
    return 0;
}
/* 请在这里填写答案 */

输入样例:
第1行输入结点数vexnum和边数arcnum。接下来依次输入arcnum行,每行输入3个值,前两个字符表示结点编号,后一个数表示两个结点之间边的权值。最后一行输入源点编号。

6 8
0 5 100
0 2 10
0 4 30
1 2 5
2 3 50
3 5 10
4 3 20
4 5 60
0

输出样例:
输出源点到终点(按终点编号递增顺序)的最短路径,无路径输出0。

0
10
50
30
60

答案

//c++
//单源最短路径(Dijistra算法)
void bfs(){
    
    
    int visit[MAXN];
    memset(visit,0,sizeof(visit));
    visit[v]=1;//标记源点为已访问
    dist[v]=0;//更新源点到源点距离为0
    int temp=v;//指向刚访问的顶点
    //循环n-1次,用n-1个顶点更新剩下的距离
    for(int i=0;i<n-1;i++){
    
    
        //每次更新,都遍历所有没有访问过的顶点
        int minDist=INF;//记录最小距离
        int minN=-1;
        for(int j=0;j<n;j++){
    
    
            if(visit[j]==1){
    
    
                continue;
            }
            if(dist[temp]+a[temp][j]<dist[j]){
    
    
                dist[j]=dist[temp]+a[temp][j];
            }
            if(dist[j]<minDist){
    
    
                minDist=dist[j];
                minN=j;
            }
        }
        //更新加入结点
        visit[minN]=1;
        temp=minN;
    }
}
//打印源点到各个顶点的距离
void dispallpath(){
    
    
    for(int i=0;i<n;i++){
    
    
        if(i==v){
    
    
            continue;
        }
        if(dist[i]!=INF){
    
    
            cout<<dist[i]<<endl;
        }else{
    
    
            cout<<0<<endl;
        }
    }
}

编程题

7-1 第k小元素

题目

给定一个大小为n(1≤n≤1000000)且无序的整型数组,数组中可能存在相同元素,请找出该数组第k(1≤k≤n)小的元素,注意这里的第k小元素指的是按从小到大排序后的第k个位置上的元素。

输入格式:
每个输入文件为一个测试用例,每个文件的第一行给出两个正整数n和k,第二行给出n个整数,其间以空格分隔。

输出格式:
输出第k小元素的值。

输入样例:

10 4
2 3 5 12 4 9 3 8 2 9

输出样例:

3

答案

//c++
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n;//数组长度
int k;//求解第k小
vector<int> v;//存储无序整数数组
bool cmp(int x,int y){
    
    
    return x<y;
}
int main(){
    
    
    cin>>n>>k;
    for(int i=0;i<n;i++){
    
    
        int temp;
        cin>>temp;
        v.push_back(temp);
    }
    sort(v.begin(),v.end(),cmp);
    cout<<v[k-1]<<endl;
    return 0;
}

7-2 二分搜索

题目

输入样例:
第一行输入一个数n,第二行输入n个数,第三行输入要查的值。

12
11 14 23 25 68 78 84 97 155 201 310 479
97

输出样例:
输出key在序列中的位置。

7

答案

//c++
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n;//数字个数
int m;//要查询的值
vector<int> v;

int myFind(int start,int end){
    
    
    int mid=(start+end)/2;
    if(v[mid]==m){
    
    
        return mid;
    }else if(v[mid]<m){
    
    
        return myFind(mid+1,end);
    }else{
    
    
        return myFind(start,mid-1);
    }
}

int main(){
    
    
    cin>>n;
    for(int i=0;i<n;i++){
    
    
        int temp;
        cin>>temp;
        v.push_back(temp);
    }
    cin>>m;
    //调用库函数查找
    // auto it=find(v.begin(),v.end(),m);
    // if(it!=v.end()){
    
    
    //     cout<<distance(v.begin(),it);
    // }else{
    
    
    //     cout<<0;
    // }

    //二分查找实现
    cout<<myFind(0,n-1);
    return 0;
}

7-3 快速幂运算

题目

编程求x^y 最后三位数表示的整数

输入格式:
输入在一行中给出两个整数x和y,其中1<=x,y<=1000000000

输出格式:
输出占一行,是x^y 的最后三位表示的整数。

输入样例1:
在这里给出一组输入。例如:

2 3

输出样例1:
在这里给出相应的输出。例如:

8

输入样例2:
在这里给出一组输入。例如:

12  6

输出样例2:
在这里给出相应的输出。例如:

984

答案

//c++
//直接用pow做只能拿部分分,因为快速幂范围很大
//由于结果只求最后三位,故可以用二分法求解,部分积只保留最后三位,保证不溢出
#include<iostream>
using namespace std;
long x,y;

int quickPow(int a,int b){
    
    
    if(b==1){
    
    
        return a%1000;
    }else{
    
    
        int r=quickPow(a,b/2);
        if(b%2==0){
    
    
            return r*r%1000;
        }else{
    
    
            return r*r*a%1000;
        }
    }
}

int main(){
    
    
    cin>>x>>y;
    int result=quickPow(x,y);
    cout<<result<<endl;
    return 0;
}

7-4 h0154.加勒比海盗船——最优装载问题

题目

有一天,海盗们截获了一艘装满各种各样古董的 货船,每一件古董都价值连城,一旦打碎就失去了它 的价值。虽然海盗船足够大,但载重量为 C,每件古 董的重量为 wi,海盗们该如何把尽可能多数量的宝贝 装上海盗船呢?
输入格式:
第1行输入T组测试数据,每组测试数据输入载重量 c 及古董个数 n,下1行输入每个古董的重量wi,用空格分开.

输出格式:
每组能装入的古董最大数量

输入样例:

1
30 8
4 10 7 11 3 5 14 2

输出样例:

5

答案

//c++
//贪心算法
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int T;//测试数据数量
int c,n;
bool cmp(int x,int y){
    
    
    return x<y;
}
int main(){
    
    
    cin>>T;
    while(T--){
    
    
        cin>>c>>n;
        int count=0;
        int sum=0;
        vector<int> v;
        for(int i=0;i<n;i++){
    
    
            int temp;
            cin>>temp;
            v.push_back(temp);
        }
        sort(v.begin(),v.end(),cmp);
        for(int i=0;i<n;i++){
    
    
            sum+=v[i];
            if(sum<=c){
    
    
                count++;
            }else{
    
    
                break;
            }
        }
        cout<<count<<endl;
    }
    
    return 0;
}

7-5 最小生成树-prim

题目

题目给出一个无向连通图,要求求出其最小生成树的权值。

温馨提示:本题请使用prim最小生成树算法。

输入格式:
第一行包含两个整数 N(1<=N<=1x10^4 ),M(1<=M<=2x10 ^6 ) 表示该图共有 N 个结点和 M 条无向边。

接下来 M 行每行包含三个整数 Xi ,Y i​ ,Z i​ ,表示有一条长度为 Z i​ 的无向边连接结点 X i​ ,Y i​ 。
输出格式:
输出一个整数表示最小生成树的各边的长度之和。
输入样例:

4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3

输出样例:

7

答案

//c++
#include<bits/stdc++.h>
using namespace std;
#define maxx 10000010//这里需要7位数

//定一结构体数组来存每组数据 
struct Node{
    
    
    int from;
    int to;
    int val; 
    
}node[maxx]; 

int father[maxx];

bool sort_val(Node a,Node b){
    
    
    return a.val < b.val;
}

//查询元素的根节点 
int find( int a ){
    
    
    int r=a;
    while(father[r]!=r)
    r=father[r];        //找到他的前导结点
    int i=a,j;
    while(i!=r){
    
        //路径压缩算法
       j=father[i];    //记录x的前导结点
       father[i]=r;    //将i的前导结点设置为r根节点
       i=j;
    }
    return r;
}


//合并根节点不同的联通分量 
void merg(int a,int b){
    
    
    
//    int a = find(x);//查询x的根节点 
//    int b = find(y);//查询y的根节点 
    
//    if(a != b){
    
    
        father[a] = b;    
//    }     
}

int main(){
    
    
    
    int n,m;
    int sum = 0;
    
    //cin >> n >> m;
    scanf("%d%d",&n,&m);
    
    //初始化father数组 将其每个顶点的根节点设置为自己的节点号
    for(int i = 1; i <= n; i++){
    
    
        father[i] = i;
    } 
    
    for(int i = 0; i < m; i++){
    
            
        //cin >> node[i].from >> node[i].to >> node[i].val;
      scanf("%d%d%d",&node[i].from,&node[i].to,&node[i].val);
    }
        
    sort(node,node+m,sort_val);
    
//    for(int i = 0; i < m; i++){
    
    
//        cout << node[i].val << endl;
//    }

    int count = 0;
    
    for(int i = 0; i < m; i++){
    
            
        if(count == n - 1){
    
    //n个顶点需要 n - 1边
            break;
        }
        int a = find(node[i].from);
        int b = find(node[i].to);
        if(a != b){
    
    
            father[a] = b;
            sum += node[i].val;            
            count++;
        }    
       
    }
    printf("%d",sum);
    // cout << sum;     
}

7-6 最大子段和问题

题目

最大子段和问题。给定由n个整数组成的序列,求序列中子段的最大和,若所有整数均为负整数时定义最大子段和为0。

输入格式:
第一行输入整数个数n(1≤n≤10000),再依次输入n个整数。

输出格式:
输出第一行为最大子段和,第二行为子段第一个数和最后一个数在整个序列中的位序。

输入样例1:

5
-2 11 -4 13 -5 -2

输出样例1:

20
2 4

答案

//c++
#include<iostream>
#include<vector>
using namespace std;
#define MAX 10000
int n;
vector<int> v;
int result=0;
int resStart=0,resEnd=0;
int start=0,myend=0;//存储临时开始和结束
int dp[MAX]={
    
    0};//dp[i]表示前i个子段的最大值

void solve(){
    
    
    for(int i=0;i<n;i++){
    
    
        if(dp[i]>0){
    
    
            if(dp[i]+v[i]<0){
    
    
                dp[i+1]=0;
                start=i;
                myend=i;
            }else{
    
    
                dp[i+1]=dp[i]+v[i];
                myend=i;
            }
        }else{
    
    
            start=i;
            myend=i;
            dp[i+1]=max(v[i],0);
        }
        if(dp[i+1]>result){
    
    
            resStart=start;
            resEnd=myend;
            result=dp[i+1];
        }
    }
}

int main(){
    
    
    cin>>n;
    for(int i=0;i<n;i++){
    
    
        int temp;
        cin>>temp;
        v.push_back(temp);
    }
    solve();
    cout<<result<<endl;
    cout<<resStart+1<<" "<<resEnd+1<<endl;
    return 0;
}

7-7 最长子序列

题目

给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式:
第一行包含整数n,1≤n≤1000。
第二行n个整数,表示完整序列,数值范围是[-109,109]。

输出格式:
输出一个整数,表示数值严格单调递增的最长子序列的长度。

输入样例:

7
3 1 2 1 8 5 6

输出样例:

4

答案

//c++
#include<iostream>
#include<vector>
using namespace std;
#define MAX 1000
#define INF 0x3f3f3f3f
int n;//数列长度
vector<int> v;//存储数列
int dp1[MAX]={
    
    0};//dp1[i]表示前i个序列中,严格单调递增的最长子序列长度;
//dp2[i]表示前i个序列中,序列末尾元素值
int dp2[MAX]={
    
    0};
void solve(){
    
    
    dp1[0]=0;
    dp2[0]=-INF;
    dp1[1]=1;//前1个元素肯定满足单调递增
    dp2[1]=v[0];//存第一个元素的值
    for(int i=1;i<n;i++){
    
    //从第二个元素开始遍历
        int maxL=0;
        int maxS=-INF;
        //遍历0~i的dp,求解dp1[i+1]和dp2[i+1];
        for(int j=0;j<=i;j++){
    
    
            if(v[i]>dp2[j]){
    
    
                if(maxL<dp1[j]+1){
    
    
                    maxL=dp1[j]+1;
                    maxS=v[i];
                }
            }
        }
        dp1[i+1]=maxL;
        dp2[i+1]=maxS;
    }
}

int main(){
    
    
    cin>>n;
    for(int i=0;i<n;i++){
    
    
        int temp;
        cin>>temp;
        v.push_back(temp);
    }
    solve();
    cout<<dp1[n]<<endl;
    return 0;
}

7-8 N皇后问题

题目

在N×N格的国际象棋盘上摆放N个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,对于给定的N,求出有多少种合法的放置方法。
在这里插入图片描述

输入格式:
一个正整数N≤10,表示棋盘和皇后的数量;

输出格式:
一个正整数,表示对应输入行的皇后的不同放置方法。

输入样例:
在这里给出一组输入。例如:

8

输出样例:
在这里给出相应的输出。例如:

92

答案

//c++
//回溯法
#include<iostream>
#include<vector>
using namespace std;
#define MAX 10
int n;
int arr[MAX][MAX]={
    
    0};
int result=0;
struct Dir{
    
    
    int x;
    int y;
};
Dir dir[4];
//判断是否越界
bool inBorder(int x,int y){
    
    
    if(x<0||x>=n||y<0||y>=n){
    
    
        return false;
    }
    return true;
}
//判断是否冲突
bool safe(int x,int y){
    
    
    //判断行冲突
    for(int j=0;j<n;j++){
    
    
        if(j==y) continue;
        if(arr[x][j]==1) return false;
    }
    //判断列冲突
    for(int i=0;i<n;i++){
    
    
        if(i==x) continue;
        if(arr[i][y]==1) return false;
    }
    //判断斜线冲突(4个方向)
    for(int k=0;k<4;k++){
    
    
        int tempx=x+dir[k].x,tempy=y+dir[k].y;
        while(inBorder(tempx,tempy)){
    
    
            if(arr[tempx][tempy]==1){
    
    
                return false;
            }
            tempx+=dir[k].x;tempy+=dir[k].y;
        }
    }
    return true;
}


//表示摆放第count个皇后
void dfs(int count){
    
    
    //收集答案
    if(count>n){
    
    
        result++;
        return;
    }
    //第count个皇后只摆放在第count行
    int x=count-1;
    for(int y=0;y<n;y++){
    
    
        if(safe(x,y)){
    
    
            arr[x][y]=1;
            dfs(count+1);
            arr[x][y]=0;
        }    
    }
}

int main(){
    
    
    dir[0].x=-1;dir[0].y=1;  //右上
    dir[1].x=1;dir[1].y=1;  //右下 
    dir[2].x=-1;dir[2].y=-1;  //左上
    dir[3].x=1;dir[3].y=-1;  //左下
    cin>>n;
    dfs(1);
    cout<<result<<endl;
    return 0;
}

7-9 走迷宫

题目

在一个 m×n 的迷宫里,从起点开始,依次按东(右)、南(下)、西(左)、北(上) 4 个方向探索通路,直至达到终点为止。

迷宫由字符组成,W 表示墙,. 表示空地,请编写程序,输出你找到的首条通道。

输入格式

迷宫的行数 m 和列数 n (0<m,n≤100)
m 行 n 列字符
起点的行号(0 ~ m - 1)和列号(0 ~ n - 1)
终点的行号(0 ~ m - 1)和列号(0 ~ n - 1)

输出格式

若问题无解,则输出 None
若问题有解,则输出迷宫:
W 表示墙
. 表示未走过的空地
o 表示走不通而退回时经过的空地
* 表示通道经过的空地

输入样例1

5 7
W W . W W W W
W . . . W . .
W . W W W . W
W . W . . . W
W W W . W W W
0 2
4 3

输出样例1

None

输入样例2

5 7
W W . W W W W
W . . . W . .
W . W W W . W
W . . . W . W
W W W . W W W
0 2
4 3

输出样例2

W W * W W W W
W * * o W . .
W * W W W . W
W * * * W . W
W W W * W W W

答案

//c++
#include<bits/stdc++.h>
using namespace std;
int m, n;
int start_x, start_y;
int end_x, end_y;
char room[105][105];
int book[105][105];
int flag=0;
void dfs(int x, int y) {
    
    
    int next[4][2] = {
    
    
        {
    
    0,1},{
    
    1,0},{
    
    0,-1},{
    
    -1,0}
    };
    if (x == end_x && y == end_y&&flag==0) {
    
    
        
        for (int i = 0;i < m;i++) {
    
    
            for (int j = 0;j < n;j++) {
    
    
                if (j != n - 1)
                    cout << room[i][j] << " ";
                else
                    cout << room[i][j] << endl;
            }    
        }
        flag=1;
        return;
    }
    int tx=x, ty=y;
    for (int i = 0;i < 4;i++) {
    
    
        tx = x + next[i][0];
        ty = y + next[i][1];
        if (tx < 0 || ty < 0 || tx >= m || ty >= n)
            continue;
        if (book[tx][ty] == 0 && room[tx][ty] == '.') {
    
    
            book[tx][ty] = 1;
            room[tx][ty] = '*';
            dfs(tx, ty);
            room[tx][ty] = 'o';
            book[tx][ty] = 0;
        }
    }
}
int main() {
    
    
    cin >> m >> n;
    for (int i = 0;i < m;i++) {
    
    
        for (int j = 0;j < n;j++)
            cin >> room[i][j];
    }
    cin >> start_x >> start_y >> end_x >> end_y;
//     book[start_x][start_y] = 1;
//     room[start_x][start_y] = '*';
    dfs(start_x, start_y-1);
    if(flag==0)
        cout<<"None"<<endl;
}

7-10 背包问题-01背包

题目

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式:
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式:
输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例:

4 5
1 2
2 4
3 4
4 5

输出样例:

8

答案

//c++
#include<bits/stdc++.h>
using namespace std;
#define MAX 1000
int N,M;
int v[MAX]={
    
    0};
int w[MAX]={
    
    0};
int dp[MAX]={
    
    0};//dp[i]表示容量为i时,背包所能装的最大价值
int main(){
    
    
    cin>>N>>M;
    for(int i=0;i<N;i++){
    
    
        cin>>v[i]>>w[i];
    }
    //01背包
    for(int i=0;i<N;i++){
    
    //当前物品
        for(int j=M;j>=0;j--){
    
    //当前背包容量 倒推
            if(j>=v[i]){
    
    
                dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
            }
        }
    }
    cout<<dp[M]<<endl;
    return 0;
}