
第1题:数字变换
给定一个包含5个数字(0-9)的字符串,例如 “02943”,请将“12345”变换到它。 你可以采取3种操作进行变换
(1)交换相邻的两个数字
(2)将一个数字加1。如果加1后大于9,则变为0
(3)将一个数字加倍。如果加倍后大于9,则将其变为加倍后的结果除以10的余数。
最多只能用第2种操作3次,第3种操作2次 求最少经过多少次操作可以完成变换。
时间限制:1000
内存限制:65536
输入
有最多 100,000 组数据 每组数据就是包含5个数字的字符串
输出
对每组数据,输出将12345变换到给定字符串所需要的最少操作步数。如果无法变换成功,输出-1
样例输入
12435
99999
12374
样例输出
1
-1
3
提示
由于测试数据太多,如果对每组数据都从头进行搜索,就会超时。 建议先做预处理,即以“12345”作为初始状态做一遍彻底的广搜,找出“12345”经合法变换能够到达的所有字符串,并记录到达这些字符串各需要多少步操作。 然后对读入的每组数据,在上述预处理记录的结果中进行查询即可。
这个问题可以使用广度优先搜索(BFS)来解决。首先,我们以"12345"作为初始状态,通过合法的操作进行广度优先搜索,找出所有可以到达的字符串,并记录到达这些字符串所需的最少操作步数。然后,对于每组输入数据,我们可以直接在记录的结果中进行查询,找到所需的最少操作步数。
下面是一个使用C语言实现的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STATES 100000
// 定义状态结构体
typedef struct {
    char state[6];  // 保存状态的字符串
    int steps;      // 到达该状态所需的步数
} State;
// 定义队列结构体
typedef struct {
    State data[MAX_STATES];
    int front;
    int rear;
} Queue;
// 初始化队列
void initQueue(Queue *queue) {
    queue->front = queue->rear = 0;
}
// 判断队列是否为空
int isQueueEmpty(Queue *queue) {
    return queue->front == queue->rear;
}
// 入队
void enqueue(Queue *queue, State state) {
    queue->data[queue->rear] = state;
    queue->rear = (queue->rear + 1) % MAX_STATES;
}
// 出队
State dequeue(Queue *queue) {
    State state = queue->data[queue->front];
    queue->front = (queue->front + 1) % MAX_STATES;
    return state;
}
// 判断字符串是否已经存在于队列中
int isStateVisited(Queue *queue, char *state) {
    int i;
    for (i = queue->front; i != queue->rear; i = (i + 1) % MAX_STATES) {
        if (strcmp(queue->data[i].state, state) == 0) {
            return 1;
        }
    }
    return 0;
}
// 进行广度优先搜索
void bfs() {
    Queue queue;
    initQueue(&queue);
    
    State startState;
    strcpy(startState.state, "12345");
    startState.steps = 0;
    enqueue(&queue, startState);
    
    int i, j;
    while (!isQueueEmpty(&queue)) {
        State currentState = dequeue(&queue);
        int steps = currentState.steps;
        
        if (steps > 3) {
            // 超过最大步数限制,不再继续搜索
            break;
        }
        
        // 尝试进行操作1:交换相邻的两个数字
        for (i = 0; i < 4; i++) {
            for (j = i + 1; j < 5; j++) {
                State nextState = currentState;
                char temp = nextState.state[i];
                nextState.state[i] = nextState.state[j];
                nextState.state[j] = temp;
                nextState.steps = steps + 1;
                if (!isStateVisited(&queue, nextState.state)) {
                    enqueue(&queue, nextState);
                }
            }
        }
        
        // 尝试进行操作2:将一个数字加1
        for (i = 0; i < 5; i++) {
            State nextState = currentState;
            nextState.state[i] = (nextState.state[i] - '0' + 1) % 10 + '0';
            nextState.steps = steps + 1;
            if (!isStateVisited(&queue, nextState.state)) {
                enqueue(&queue, nextState);
            }
        }
        
        // 尝试进行操作3:将一个数字加倍
        for (i = 0; i < 5; i++) {
            State nextState = currentState;
            int num = nextState.state[i] - '0';
            nextState.state[i] = ((num * 2) % 10) + '0';
            nextState.steps = steps + 1;
            if (!isStateVisited(&queue, nextState.state)) {
                enqueue(&queue, nextState);
            }
        }
    }
}
int main() {
    bfs();  // 预处理,进行广度优先搜索
    
    int T;
    scanf("%d", &T);
    
    while (T--) {
        char target[6];
        scanf("%s", target);
        
        Queue queue;
        initQueue(&queue);
        
        State startState;
        strcpy(startState.state, target);
        startState.steps = 0;
        enqueue(&queue, startState);
        
        int minSteps = -1;
        
        while (!isQueueEmpty(&queue)) {
           State currentState = dequeue(&queue);
            int steps = currentState.steps;
            
            if (strcmp(currentState.state, "12345") == 0) {
                // 找到目标状态
                minSteps = steps;
                break;
            }
            
            if (steps > 3) {
                // 超过最大步数限制,不再继续搜索
                break;
            }
            
            // 尝试进行操作1:交换相邻的两个数字
            for (i = 0; i < 4; i++) {
                for (j = i + 1; j < 5; j++) {
                    State nextState = currentState;
                    char temp = nextState.state[i];
                    nextState.state[i] = nextState.state[j];
                    nextState.state[j] = temp;
                    nextState.steps = steps + 1;
                    if (!isStateVisited(&queue, nextState.state)) {
                        enqueue(&queue, nextState);
                    }
                }
            }
            
            // 尝试进行操作2:将一个数字加1
            for (i = 0; i < 5; i++) {
                State nextState = currentState;
                nextState.state[i] = (nextState.state[i] - '0' + 1) % 10 + '0';
                nextState.steps = steps + 1;
                if (!isStateVisited(&queue, nextState.state)) {
                    enqueue(&queue, nextState);
                }
            }
            
            // 尝试进行操作3:将一个数字加倍
            for (i = 0; i < 5; i++) {
                State nextState = currentState;
                int num = nextState.state[i] - '0';
                nextState.state[i] = ((num * 2) % 10) + '0';
                nextState.steps = steps + 1;
                if (!isStateVisited(&queue, nextState.state)) {
                    enqueue(&queue, nextState);
                }
            }
        }
        
        printf("%d\n", minSteps);
    }
    
    return 0;
}
这个代码首先进行预处理,使用广度优先搜索找出所有可以从"12345"变换到的字符串,并记录到达这些字符串所需的最少操作步数。然后,对于每组输入数据,使用另一个队列进行广度优先搜索,找到从目标字符串到"12345"所需的最少操作步数,并输出结果。
请注意,这个代码使用了队列来实现广度优先搜索,并使用了一个布尔数组来记录已经访问过的状态,以避免重复搜索。同时,为了优化性能,在进行广度优先搜索时,限制了最大步数为3,超过这个步数的状态将不再考虑。
第2题:圣诞老人的礼物
圣诞节来临了,在城市A中圣诞老人准备分发糖果,现在有多箱不同的糖果,每箱糖果有自己的价值和重量,每箱糖果都可以拆分成任意散装组合带走。圣诞老人的驯鹿最多只能承受一定重量的糖果,请问圣诞老人最多能带走多大价值的糖果。
时间限制:1000
内存限制:65536
输入
第一行由两个部分组成,分别为糖果箱数正整数n(1 = n = 100),驯鹿能承受的最大重量正整数w(0 w 10000),两个数用空格隔开。其余n行每行对应一箱糖果,由两部分组成,分别为一箱糖果的价值正整数v和重量正整数w,中间用空格隔开。
输出
输出圣诞老人能带走的糖果的最大总价值,保留1位小数。输出为一行,以换行符结束。
样例输入
4 15
100 4
412 8
266 7
591 2
样例输出
1193.0
最容易实现的方法是使用贪心算法。下面是使用贪心算法的简单实现:
#include <stdio.h>
#define MAX_N 100
// 定义糖果的结构体
typedef struct {
    int value;
    int weight;
} Candy;
// 贪心算法函数
double greedyAlgorithm(Candy candies[], int n, int maxWeight) {
    // 根据单位重量的价值进行排序
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            double valuePerWeight1 = (double)candies[j].value / candies[j].weight;
            double valuePerWeight2 = (double)candies[j + 1].value / candies[j + 1].weight;
            
            if (valuePerWeight1 < valuePerWeight2) {
                // 交换糖果的位置
                Candy temp = candies[j];
                candies[j] = candies[j + 1];
                candies[j + 1] = temp;
            }
        }
    }
    
    double maxTotal = 0;
    int currentWeight = 0;
    
    for (int i = 0; i < n; i++) {
        if (currentWeight + candies[i].weight <= maxWeight) {
            // 将当前糖果全部带走
            maxTotal += candies[i].value;
            currentWeight += candies[i].weight;
        } else {
            // 部分带走当前糖果
            double remainingWeight = maxWeight - currentWeight;
            double remainingValue = (double)candies[i].value * remainingWeight / candies[i].weight;
            maxTotal += remainingValue;
            break;
        }
    }
    
    return maxTotal;
}
int main() {
    int n, maxWeight;
    Candy candies[MAX_N];
    
    // 读取输入
    scanf("%d %d", &n, &maxWeight);
    for (int i = 0; i < n; i++) {
        scanf("%d %d", &candies[i].value, &candies[i].weight);
    }
    
    // 使用贪心算法
    double maxTotal = greedyAlgorithm(candies, n, maxWeight);
    
    // 输出结果
    printf("%.1lf\n", maxTotal);
    
    return 0;
}
这个方法的思路是先将糖果按照单位重量的价值进行排序,然后从价值最高的糖果开始,依次尽可能地带走糖果,直到达到驯鹿能够承受的最大重量。如果某个糖果无法完全带走,则部分带走该糖果。最后,将带走的糖果的总价值进行累加,即为圣诞老人能够带走的糖果的最大总价值。
第3题:忍者道具
忍者道具有很多种,苦无,飞镖,震爆弹。L君热衷于收集忍者道具,现在他有N个道具,每个道具的重量分别是C1、C2…CN。现在他想把这N个道具装到载重量为W的工具包里,请问他最少需要多少个工具包?
时间限制:1000
内存限制:65536
输入
第一行包含两个用空格隔开的整数,N和W。 接下来N行每行一个整数,其中第i+1行的整数表示第i个道具的重量Ci。
输出
输出一个整数,最少需要多少个工具包。
样例输入
5 1996
1
2
1994
12
29
样例输出
2
提示
对于100%的数据,1=N=18,1=Ci=W=10^8。
该题可以使用贪心算法来解决,下面是C语言代码:
#include <stdio.h>
#define MAX_N 18
int main() {
    int N, W;
    int weights[MAX_N];
    
    // 读取输入
    scanf("%d %d", &N, &W);
    for (int i = 0; i < N; i++) {
        scanf("%d", &weights[i]);
    }
    
    // 贪心算法求解
    int numBags = 0;
    int currentWeight = 0;
    
    for (int i = 0; i < N; i++) {
        if (currentWeight + weights[i] <= W) {
            // 当前工具包可以容纳该道具
            currentWeight += weights[i];
        } else {
            // 当前工具包无法容纳该道具,需要使用新的工具包
            numBags++;
            currentWeight = weights[i];
        }
    }
    
    // 若还有剩余道具未放入工具包,则需要额外的工具包
    if (currentWeight > 0) {
        numBags++;
    }
    
    // 输出结果
    printf("%d\n", numBags);
    
    return 0;
}
该算法的思路是按照道具的重量顺序进行遍历,如果当前工具包的载重量可以容纳当前道具,则将道具放入工具包中;否则,需要使用新的工具包来装载该道具。最后,如果还有剩余的道具未放入工具包,则需要额外的工具包。输出最少需要的工具包数量即为解。
第4题:求逆序对问题
给定N个数的序列a1,a2,…aN,定义一个数对(ai, aj)为“重要逆序对”的充要条件为 i j 且 ai 2aj。求给定序列中“重要逆序对”的个数。
时间限制:1000
内存限制:256000
输入
本题有多个测试点,每个测试点分为两行:第一行为序列中数字的个数N(1 ≤ N ≤ 200000),第二行为序列a1, a2 … aN(0 ≤a ≤ 10000000),由空格分开。N=0表示输入结束。
输出
每个测试点一行,输出一个整数,为给序列中“重要逆序对”的个数。
样例输入
10
0 9 8 7 6 5 4 3 2 1
0
样例输出
16
提示
请注意答案范围,如果使用printf输出long long类型,请用%lld
以下是使用分治技术来解决求逆序对问题的C语言代码:
#include <stdio.h>
#define MAX_N 200000
long long merge(int arr[], int temp[], int left, int mid, int right) {
    int i = left;     // 左子数组的起始索引
    int j = mid + 1;  // 右子数组的起始索引
    int k = left;     // 合并后数组的起始索引
    long long count = 0;  // 逆序对的个数
    
    // 统计逆序对的个数
    while (i <= mid && j <= right) {
        if (arr[i] > 2 * arr[j]) {
            count += (mid - i + 1);
            j++;
        } else {
            i++;
        }
    }
    
    // 归并排序并计算逆序对
    i = left;
    j = mid + 1;
    k = left;
    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    
    while (i <= mid) {
        temp[k++] = arr[i++];
    }
    
    while (j <= right) {
        temp[k++] = arr[j++];
    }
    
    for (i = left; i <= right; i++) {
        arr[i] = temp[i];
    }
    
    return count;
}
long long mergeSort(int arr[], int temp[], int left, int right) {
    long long count = 0;
    
    if (left < right) {
        int mid = (left + right) / 2;
        
        count += mergeSort(arr, temp, left, mid);
        count += mergeSort(arr, temp, mid + 1, right);
        
        count += merge(arr, temp, left, mid, right);
    }
    
    return count;
}
int main() {
    int N;
    int arr[MAX_N];
    int temp[MAX_N];
    
    while (1) {
        // 读取输入
        scanf("%d", &N);
        if (N == 0) {
            break;
        }
        
        for (int i = 0; i < N; i++) {
            scanf("%d", &arr[i]);
        }
        
        // 使用分治技术求解逆序对个数
        long long count = mergeSort(arr, temp, 0, N - 1);
        
        // 输出结果
        printf("%lld\n", count);
    }
    
    return 0;
}
该算法使用归并排序的思想,在归并排序的过程中统计逆序对的个数。在归并的过程中,通过比较左子数组和右子数组的元素来统计逆序对的个数,并将左子数组和右子数组合并成一个有序数组。最后,返回逆序对的个数作为解。
















![[已解决] wget命令出现Unable to establish SSL connection.错误](https://img-blog.csdnimg.cn/c062966a0e6f4da0b54e93117ee9f99b.png)


