SDUT OJ《算法分析与设计》贪心算法

A - 汽车加油问题

Description

一辆汽车加满油后可行驶n公里。旅途中有若干个加油站。设计一个有效算法,指出应在哪些加油站停靠加油,使沿途加油次数最少。并证明算法能产生一个最优解。
对于给定的n和k个加油站位置,计算最少加油次数。

Input

输入数据的第一行有2 个正整数n和k(n≤5000,k≤1000),表示汽车加满油后可行驶n公里,且旅途中有k个加油站。接下来的1 行中,有k+1 个整数,表示第k个加油站与第k-1 个加油站之间的距离。第0 个加油站表示出发地,汽车已加满油。第k+1 个加油站表示目的地。

Output

将计算出的最少加油次数输出。如果无法到达目的地,则输出“No Solution!”。

Samples

Sample #1
Input 
Output 
7 7
1 2 3 4 5 1 6 6
4
#include<bits/stdc++.h>
using namespace std;
const int N = 1005;
int a[N];
//0 1     1    1     1    1   1   1   0
// 1   5     4    5     4   3   3   3
int main()
{
    int n, k;
    int cnt = 0;
    bool flag = 0;
    cin >> n >> k;
    for(int i = 0; i <= k; i++){
        cin >> a[i];
        if(a[i] > n){
            flag = 1;
        }
    }
    int d = n;// 留n以后用
    if(flag) cout << "No Solution!" << "\n";
    else{
        for(int i = 0; i <= k; i++){
            if(d >= a[i]){
                d -= a[i];
            }
            else{
                d = n;
                cnt++;
                d -= a[i];
            }
        }
        cout << cnt << "\n";
    }
    return 0;
}

B - 多元Huffman编码问题

Description

在一个操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次至少选2 堆最多选k堆石子合并成新的一堆,合并的费用为新的一堆的石子数。试设计一个算法,计算出将n堆石子合并成一堆的最大总费用和最小总费用。
对于给定n堆石子,计算合并成一堆的最大总费用和最小总费用。

Input

输入数据的第1 行有2 个正整数n和k(n≤100000,k≤10000),表示有n堆石子,每次至少选2 堆最多选k堆石子合并。第2 行有n个数(每个数均不超过 100),分别表示每堆石子的个数。

Output

将计算出的最大总费用和最小总费用输出,两个整数之间用空格分开。

Samples

Sample #1
Input 
Output 
7 3
45 13 12 16 9 5 22
593 199

Hint

请注意数据范围是否可能爆 int。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main()
{
    int n, k;
    cin >> n >> k;
    priority_queue<int>q1;// 降序,计算最大值
    priority_queue<int, vector<int>, greater<int>>q2;// 升序,计算最小值
    for(int i = 0; i < n; i++){
        int x;
        cin >> x;
        q1.push(x);
        q2.push(x);
    }
    LL maxn = 0, minn = 0;
    while(q1.size() > 1){
        LL sum = 0;
        int a = q1.top();
        q1.pop();
        int b = q1.top();
        q1.pop();
        sum += (a + b);
        maxn += sum;
        q1.push(sum);
    }
    while(q2.size() % (k-1) != 1){
        q2.push(0);
    }
    while(q2.size() > 1){
        LL sum = 0;
        for(int i = 0; i < k; i++){
            sum += q2.top();
            q2.pop();
        }
        minn += sum;
        q2.push(sum);
    }
    cout << maxn << ' ' << minn << endl;
    return 0;
}

C - 装船问题

Description

王小二毕业后从事船运规划工作,吉祥号货轮的最大载重量为M吨,有10种货物可以装船。第i种货物有wi吨,总价值是pi。王小二的任务是从10种货物中挑选若干吨上船,在满足货物总重量小于等于M的前提下,运走的货物的价重比最大。

Input

输入数据的第一行有一个正整数M(0 < M < 10000),表示所有货物最大载重量。在接下来的10行中,每行有若干个数(中间用空格分开),第i行表示的是第i种货物的货物的总价值pi ,总重量wi。(pi是wi的整数倍,0 < pi , wi < 1000)

Output

输出一个整数,表示可以得到的最大价值。

Samples

Sample #1
Input 

100

10 10

20 10

30 10

40 10

50 10

60 10

70 10

80 10

90 10

100 10

Output 
550

Hint

价重比:计算其价值与重量之比

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 12;

struct node{// 用结构体更方便
    int p, w;
    int c;
}huo[N], t;
int main()
{
    int m;
    cin >> m;
    for(int i = 0; i < 10; i++){
        cin >> huo[i].p >> huo[i].w;
        huo[i].c = huo[i].p / huo[i].w;// 算出价重比
    }
    for(int i = 0; i < 9; i++){
        for(int j = i; j < 10; j++){
            if(huo[i].c < huo[j].c){
                t = huo[i];
                huo[i] = huo[j];
                huo[j] = t;
            }
        }
    }
    int sum = 0;
    for(int i = 0; i < 10; i++){
        if(m >= huo[i].w){
            m -= huo[i].w;
            sum += huo[i].p;
        }else{
            sum += huo[i].c * m;// !!!
            break;
        }
    }
    cout << sum << endl;
    return 0;
}

D - 活动选择

Description

学校的大学生艺术中心周日将面向全校各个学院的学生社团开放,但活动中心同时只能供一个社团活动使用,并且每一个社团活动开始后都不能中断。现在各个社团都提交了他们使用该中心的活动计划(即活动的开始时刻和截止时刻)。请设计一个算法来找到一个最佳的分配序列,以能够在大学生艺术中心安排不冲突的尽可能多的社团活动。
比如有5个活动,开始与截止时刻分别为:

 



最佳安排序列为:1,4,5。

Input

第一行输入活动数目n(0<n<100);
以后输入n行,分别输入序号为1到n的活动使用中心的开始时刻a与截止时刻b(a,b为整数且0<=a,b<24,a,b输入以空格分隔)。

Output

输出最佳安排序列所包含的各个活动(按照活动被安排的次序,两个活动之间用逗号分隔),如果有多个活动安排序列符合要求输出字典序最小的序列。

Samples

Sample #1
Input 
Output 
6
8 10
9 16
11 16
14 15
10 14
7 11
1,5,4
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 110;

struct node{// 用结构体更方便
    int s, e;
    int id, flag;
}a[N], t;
int main()
{
    int n;
    cin >> n;
    for(int i = 0; i < n; i++){
        cin >> a[i].s >> a[i].e;
        a[i].id = i + 1;
        a[i].flag = 0;
    }
    for(int i = 0; i < n - 1; i++){// 只能这样排序哦,
        for(int j = 0; j < n - 1 - i; j++){
            if(a[j].e > a[j+1].e){
                t = a[j];
                a[j] = a[j+1];
                a[j+1] = t;
            }
        }
    }
    int start = 0;
    for(int i = 0; i < n; i++){
        if(a[i].s >= start){
            a[i].flag = 1;
            start = a[i].e;
        }
    }
    cout << a[0].id;
    for(int i = 1; i < n; i++){
        if(a[i].flag){
            cout << ',' << a[i].id;
        }
    }
    cout << endl;
    return 0;
}

E - 最优合并问题

Description

给定k 个排好序的序列s1 , s2,……, sk , 用2 路合并算法将这k 个序列合并成一个序列。假设所采用的2 路合并算法合并2 个长度分别为m和n的序列需要m + n -1次比较。试设计一个算法确定合并这个序列的最优合并顺序,使所需的总比较次数最少。
为了进行比较,还需要确定合并这个序列的最差合并顺序,使所需的总比较次数最多。
对于给定的k个待合并序列,计算最多比较次数和最少比较次数合并方案。

Input

输入数据的第一行有1 个正整数k(k≤1000),表示有k个待合并序列。接下来的1 行中,有k个正整数,表示k个待合并序列的长度。

Output

输出两个整数,中间用空格隔开,表示计算出的最多比较次数和最少比较次数。

Samples

Sample #1
Input 

4

5 12 11 2

Output 
78 52
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e3 + 10;
int a[N], b[N];
bool cmp(int a, int b){
    return a > b;
}
int main()
{
    int k;
    cin >> k;
    for(int i = 0; i < k; i++){
        cin >> a[i];
        b[i] = a[i];
    }
    sort(a, a + k);// 升序
    sort(b, b + k, cmp);// 降序
    int minn = 0, maxn = 0;
    for(int i = 0; i < k - 1; i++){
        a[i+1] += a[i];
        minn += a[i+1];
        sort(a+i+1, a+k);

        b[i+1] += b[i];
        maxn += b[i+1];
        sort(b+i+1, b+k, cmp);
    }
    cout << maxn - k + 1 << ' ' << minn - k + 1 << endl;
    return 0;
}

F - 区间覆盖问题

Description

设x1 , x2 ,…… , xn 是实直线上的n 个点。用固定长度的闭区间覆盖这n 个点,至少需要多少个这样的固定长度闭区间?
对于给定的实直线上的n个点和闭区间的长度k,设计解此问题的有效算法,计算覆盖点集的最少区间数,并证明算法的正确性。

Input

输入数据的第一行有2 个正整数n和k(n≤10000,k≤100),表示有n个点,且固定长度闭区间的长度为k。接下来的1 行中,有n个整数,表示n个点在实直线上的坐标(可能相同)。

Output

输出一个整数,表示计算出的最少区间数输出。

Samples

Sample #1
Input 

7 3

1 2 3 4 5 -2 6

Output 
3
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e4 + 10;
int a[N];
int main()
{
    int n, k;
    int cnt = 1;
    cin >> n >> k;
    for(int i = 0; i < n; i++){
        cin >> a[i];
    }
    sort(a, a + n);
    // -2 1 2 3 4 5 6
    int x = k;
    for(int i = 1; i < n; i++){
        if(x >= (a[i]-a[i-1])){
            x -= (a[i]-a[i-1]);
        }else{
            x = k;
            cnt ++;
        }
    }
    cout << cnt << endl;
    return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SDUT-OJ(Software Development University of Tsinghua Online Judge)是一个在线编程平台,提供给清华大学软件学院的学生和爱好者练习和算法问题的环境,其中包括各种计算机科学题目,包括数据结构、算法、图形等。对于"最小生成树"(Minimum Spanning Tree, MST)问题,它是图论中的经典问题,目标是从一个加权无向图中找到一棵包含所有顶点的树,使得树的所有边的权重之和最小。 在C语言中,最常见的是使用Prim算法或Kruskal算法来求最小生成树。Prim算法从一个顶点开始,逐步添加与当前生成树相连且权重最小的边,直到所有顶点都被包含;而Kruskal算法则是从小到大对所有边排序,每次选取没有形成环的新边加入到树中。 如果你想了如何用C语言实现这些算法,这里简单概括一下: - 通常使用优先队列(堆)来存储边和它们的权重,以便快速查找最小值。 - 从任意一个顶点开始,遍历与其相邻的边,若新边不形成环,就更新树,并将新边加入优先队列。 - Kruskal算法: - 先将所有的边按照权重升序排序。 - 创建一个空的最小生成树,然后依次取出排序后的边,如果这条边连接的两个顶点不在同一个连通分量,则将其添加到树中。 如果你需要更详细的代码示例,或者有具体的问题想了(比如如何处理环、如何实现优先队列等),请告诉我,我会为你提供相应的帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值