HNUCM-2023年春季学期《算法分析与设计》练习16

0-1背包问题(回溯法)

题目描述

有n个物品,第i个物品重量为wi,价值为vi,现有一背包容量为C,要求把物品装入背包得到最大价值,并且要求出这些选取的物品。 要求用回溯法求解。

输入

多组测试数据,请处理到文件尾,一个整数表示物品的数量n,后一行有n个整数,代表价值,再后一行有n个整数,代表重量,最后有一个整数C代表背包容量,1<=n<=15,1<=vi<=30,1<=wi<=30,1<=C<=80。

输出

背包的最大总价值和所选取的物品,如果选取的方案有多种,请输出字典序最小的那种方案,每组测试数据应输出一行,在这里字典序最小的意思是,我们假设存在两种不同方案S,T所能得到的总价值相同且是最大的,对于方案S种选取|S|种物品,方案T选取|T|种物品,对于i=1,2...j-1,我们有si = ti,但sj < tj,则方案的S的字典序比方案T的字典序要小。由于没有使用special judge,所以如果选取得方案是S,请按照从小到大的顺序输出方案S中的物品下标。

样例输入 Copy
5
6 3 6 5 4
2 2 4 6 5
8
样例输出 Copy

15 1 2 3
def bound(i):
    left=C-cw;
    b=cv;
    while i<=n and w[i]<=left:
        b+=v[i]
        left-=w[i]
        i+=1
    if i<=n:
        b+=left*v[i]/w[i]
    return b
def backtrack(t):
    global cw,bestv,cv
    if t>=n:
        if bestv<cv:
            bestv=cv
            for j in range(n):
                bestx[j]=x[j]
    else:
        if cw+w[t]<=C:
            x[t]=1
            cv+=v[t]
            cw+=w[t]
            backtrack(t+1)
            x[t]=0
            cv-=v[t]
            cw-=w[t]
        if bound(t+1)>bestv:
            x[t]=0
            backtrack(t+1)
while True:
    n=int(input())
    v=list(map(int,input().split()))
    w=list(map(int,input().split()))
    C=int(input())
    w.append(0)
    v.append(0)
    bestv=0
    cw=0
    cv=0
    v.sort(key=lambda v[0]/w[0] reverse=True)
    print(v)
    x=[0 for _ in range(n)]
    bestx=[0 for _ in range(n)]
    backtrack(0)
    print(bestv, end=" ")
    for j in range(n):
        if bestx[j]==1:
            print(j+1,end=" ")
    

旅行售货员(TSP)

题目描述

有若干个城市,任何两个城市之间的距离都是确定的,现要求一旅行商从某城市出发必须经过每一个城市且只在一个城市逗留一次,最后回到出发的城市,问如何事先确定一条最短的线路以保证路径最短?

输入

输入两个整数n,m分别代表城市数量,边数量,2<=n<=8,n-1<=m<=2*n
接下来m行每行输入有3个整数x,y,z代表x和y城市之间有一条长为z的路,保证输入的图是连通的,旅行商总是从1城市出发。

输出

要求求出最短的路径,和该路径的长度,如果不存在这样的路径你只需要输出-1。

样例输入 Copy
4 6
1 2 30
1 3 6
1 4 4
2 3 5
2 4 10
3 4 20
样例输出 Copy
1 3 2 4 1
25
#include<bits/stdc++.h>
using namespace std;
const int maxv=110;
int n,k;
int m[maxv];
int bestx[maxv];
int bestc=-1;
int noedge=-1;
double cc;
double g[maxv][maxv];
int ans;
void backstrack(int i){
    if(i==n){
        if(g[m[n-1]][m[n]]!=noedge&&g[m[n]][1]!=noedge&&(bestc==-1||cc+g[m[n-1]][m[n]]+g[m[n]][1]<bestc)){
            for(int j=1;j<=n;++j){
                bestx[j]=m[j];
            }
            ++ans;
            bestc=cc+g[m[n-1]][m[n]]+g[m[n]][1];
        }
    }else{
        for(int j=i;j<=n;++j){
            if(g[m[i-1]][m[j]]!=noedge&&(bestc==-1||cc+g[m[i-1]][m[j]]<bestc)){
                swap(m[i],m[j]);
                cc+=g[m[i-1]][m[i]];
                backstrack(i+1);
                cc-=g[m[i-1]][m[i]];
                swap(m[i],m[j]);
            }
        }
    }
}
double tsp(){
    int i;
    for(i=1;i<=n;++i){
        m[i]=i;
    }
    bestc=-1;
    cc=0;
    backstrack(2);
    return bestc;
}
int main()
{
    cin>>n>>k;
    ans=0;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            g[i][j]=g[j][i]=-1;
        }
    }
    for(int i=1;i<=k;++i){
        int x,y,z;
        cin>>x>>y>>z;
        g[x][y]=z;
        g[y][x]=z;
    }
    double res=tsp();
    if(ans==0){
        cout<<-1<<endl;
        return 0;
    }
    for(int i=1;i<=n;++i){
        cout<<bestx[i]<<" ";
    }
    cout<<bestx[1]<<endl;
    cout<<bestc<<endl;
    return 0;
}

挑选奖品

m,n=map(int,input().split())
a=list(map(int,input().split()))
a.sort(reverse=True)
print(sum(a[:m]))

排列蛋卷

题目描述

刚考完研的TC同学这段时间在家闲得慌,因此他决定学点新手艺。今天他学习的内容是:做蛋卷。
由于是第一次做蛋卷,TC同学做出来蛋卷长短不一。看着这些长度都不一样的蛋卷,TC同学的强迫症又犯了。他希望能够拿出其中部分蛋卷,使得留下来的蛋卷能够按照长度从大到小的次序排列
请问他最少需要拿出多少根蛋卷

输入

单组输入,对于每一组测试数据,第1行N表示蛋卷的总数量(n<=1000)。 
第2行包含N个正整数,分别表示每一根蛋卷的长度。(单位:厘米) 
保证在同一组输入数据中每一根蛋卷的长度均不一样。

输出

输出最少需要拿出的蛋卷数量。

样例输入 Copy
5
15 18 17 11 12
样例输出 Copy
2
n=int(input())
a=[1]*n
b=list(map(int,input().split()))
for i in range(n):
    maxlen=0
    for j in range(i-1,-1,-1):
         if b[i]<b[j] and a[j]>maxlen:
            maxlen=a[j]
    a[i]=maxlen+1
print(n-max(a))

最大收益

题目描述

小X是一位精明的投资人,他每次都能够做出正确的投资判断。
现在有N个项目,每个项目的总投资额和总收益已知,并且每一个项目都允许小X只投资一部分,当然也就只能拿到一部分收益。 
现在小X决定拿出M万元钱来进行投资,请你帮他设计一个算法来计算最大收益和

输入

单组输入,对于每一组数据,第1行包含两个正整数,分别是M和N,其中M<=10^6,N<=100。
接下来N行每行均包含两个正数(不一定是正整数),第1数字表示第N个项目的总投资额(万元),第2个数字表示第N个项目的总收益(万元),两个数字之间用空格隔开。 

输出

小X可以获得的最大收益和(万元,结果四舍五入保留两位小数)。

样例输入 Copy
100 4
50 10 
20 10 
40 10 
50 20
样例输出 Copy
37.50
while True:
    try:
        n, d = map(float, input().split())
        cake=[]
        res=0.00
        for i in range(int(d)):
            a,b=map(float,input().split())
            cake.append([a,b])
        cake.sort(reverse=True, key=lambda x: x[1] / x[0])
        cnt = 0
        while n > 0:
            w=cake[cnt][0]
            v=cake[cnt][1]
            cnt = cnt + 1
            if n >= w:
                res= res + v
                n=n-w
            else:
                res= res + n / w * v
                n=0
        print("%.2f" % res)
    except:
        break

X星人的基因

题目描述

X星人的基因由A、B、C、D、E五种不同的结构组合而成。
如果两个性别不同的X星人的基因序列相似度大于50%,按照X星的法律他们是禁止结婚的,等于50%据说还是可以的。
那么基因的相似度怎么计算呢?分别从两个人身上取长度均为N的基因片段,如果它们的最长公共子序列为M,则相似度=M/N。是不是很简单呢?
现在给你两段X星人的基因序列片段,请你判断他们是不是可以结婚?

输入

每一组测试数据包含3行,
第1行数字N表示待比较基因序列片段的长度,N<=10^3。
第2行和第3行为两个长度为N的基因序列片段。
输入0表示结束。

输出

两个X星人是否可以结婚,如果可以输出”Yes“,如果不可以输出”No“。

样例输入 Copy
8
A B C D E A B C
A C C D C B A E
6
A B C D E E
A E D C B B
0
样例输出 Copy
Yes
Yes
while True:
    n=int(input())
    s1,s2=''.join(input().split()),''.join(input().split())
    dp=[[0]*(n+1) for _ in range(n+1)]
    for i in range(1,n+1):
        for j in range(1,n+1):
            dp[i][j]=dp[i-1][j-1]+1 if s1[i-1]==s2[j-1] else max(dp[i-1][j],dp[i][j-1])
    print('No') if dp[n][n]/n>0.5 else print('Yes')
     

X星人的礼物

n,m=map(int,input().split())
a=list(map(int,input().split()))
a.sort()
ans=0
i=0
j=n-1
while i<j:
    if a[i]+a[j]<=m:
        ans,i,j=ans+1,i+1,j-1
    else:
        ans,j=ans+1,j-1
print(ans) if i!=j else print(ans+1)

X星人的股票

n,m=map(int,input().split())
a=list(map(int,input().split()))
a.sort()
ans=0
i=0
j=n-1
while i<j:
    if a[i]+a[j]<=m:
        ans,i,j=ans+1,i+1,j-1
    else:
        ans,j=ans+1,j-1
print(ans) if i!=j else print(ans+1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您讲解一下 HNUCM 最小生成树(Prim)的算法过程。 首先,Prim 算法是一种贪心算法,用于求解带权无向连通图的最小生成树。下面是具体的算法过程: 1. 选取任意一个顶点作为起点,将其加入到生成树中。 2. 找到与已经加入到生成树中的顶点相邻的所有顶点,并计算它们与生成树的距离。 3. 从这些顶点中选取距离最小的顶点,将其加入到生成树中。 4. 重复步骤2和3,直到生成树中包含了所有的顶点。 在具体实现时,可以使用一个数组来记录每个顶点是否已经加入到生成树中,以及另一个数组来记录每个顶点与生成树的距离。每次选择距离最小的顶点时,可以使用一个最小堆来优化。 至于 HNUCM 最小生成树 Prim 算法的具体实现,可以参考以下代码: ```python import heapq def prim(graph): n = len(graph) visited = [False] * n distance = [float('inf')] * n distance[0] = 0 heap = [(0, 0)] result = 0 while heap: (d, u) = heapq.heappop(heap) if visited[u]: continue visited[u] = True result += d for v, weight in graph[u]: if not visited[v] and weight < distance[v]: distance[v] = weight heapq.heappush(heap, (weight, v)) return result ``` 这段代码实现了 HNUCM 最小生成树 Prim 算法的过程,其中 graph 是一个邻接表表示的带权无向连通图,每个元素是一个二元组 (v, w),表示从节点 u 到节点 v 有一条边权为 w 的边。算法的返回值是最小生成树的总权值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值