题解 | #云服务器资源优化#
云服务器资源优化
https://www.nowcoder.com/practice/28976c0198844c13ab4e2e7c04e3979e
REALHW79 云服务器资源优化
题目链接
题目描述
您是一位顶尖的云计算工程师,负责为一个客户在数据中心部署一组高性能计算服务。您拥有一台物理服务器,但这台服务器的总功耗预算是有限的。
现在,您需要从一系列待选服务中,挑选出一个最优的组合进行部署,以在满足功耗限制的前提下,实现最大的计算价值。
现有 个待部署的计算服务,按
到
升序编号,服务器的总功耗预算为
。
对于每个服务 ,我们有两个关键指标:
- 功耗(Power Consumption):
- 计算价值(Computing Value):
您的任务是选择一个服务的组合,同时满足以下两个条件:
- 所有被选中服务的总功耗之和不得超过服务器的功耗预算
,即
。
- 所有被选中服务的总计算价值之和
应达到最大。
输出描述:
输出您最终选择的服务组合的编号,编号按从小到大排序,并用空格隔开。
择优规则(注意优先级):
- 如果没有任何一个服务组合能满足功耗预算,则输出
-1。 - 如果存在多个服务组合都能达到相同的最高总计算价值,则在这些组合中,选择总功耗最小的那个。
- 如果在满足前一个条件后仍有多个组合,则在这些组合中,选择包含服务数量最少的那个。
- 题目保证在上述所有规则的约束下,最终只会有一组唯一的最优解。
解题思路
这是一个典型的 0/1 背包问题 的变种。标准的背包问题只要求解出最大价值,而本题不仅需要记录达到最大价值所选择的物品,还需要在价值相同时应用额外的择优规则。因此,我们需要对动态规划的状态定义和转移逻辑进行扩展。
1. DP 状态定义
为了处理复杂的择优规则,DP 状态不能只存储一个价值数值。我们需要一个能包含所有择优所需信息的复合状态。可以定义一个结构体或类 State,包含三个字段:
value: 总计算价值power: 总功耗count: 服务的数量
我们使用一个二维 DP 表 dp[i][j],它存储一个 State 对象,表示在考虑前 i 个服务、且功耗预算恰好为 j 的情况下的最优状态。
2. 状态转移与比较
我们遍历每个服务 i (从 1 到 N) 和每个功耗点 j (从 0 到 C)。对于 dp[i][j],我们有两种选择:
- 不选择服务
i:此时的最优状态直接继承自只考虑前i-1个服务的情况,即dp[i-1][j]。 - 选择服务
i:这个选择仅在当前功耗预算j大于等于服务i的功耗P_i时才可行。新状态由dp[i-1][j - P_i](即不选服务i时的最优状态)加上服务i的value,power, 和count得到。
在更新 dp[i][j] 时,我们需要比较 “不选择服务 i 的状态” 和 “选择服务 i 的状态”,并选出更优的一个。这个比较必须严格遵循题目的择优规则:
value大的更优。- 若
value相等,则power小的更优。 - 若
power也相等,则count小的更优。
3. 回溯路径
当 DP 表格 dp[N][C] 填充完毕后,dp[N][j] (对于所有 ) 存储了使用全部
N 个服务在不同功耗下的最优解。我们需要:
- 找到全局最优解:遍历
dp[N][0]到dp[N][C],使用上述的择优比较逻辑,找到全局最优的State和其对应的功耗j。 - 回溯:从
dp[N][j]开始,反向推导选择了哪些服务。- 对于当前状态
dp[i][j],如果它与dp[i-1][j]不同,说明服务i在这一步被选中了。我们将服务i的编号记录下来,然后将预算j减去服务i的功耗P_i,继续考察dp[i-1][j-P_i]。 - 如果
dp[i][j]与dp[i-1][j]相同,说明服务i未被选中,我们直接继续考察dp[i-1][j]。 - 重复此过程直到
i=0。
- 对于当前状态
4. 输出
最后,将记录的服务编号排序后输出。如果最终没有选择任何服务(例如所有服务功耗都超预算),则输出 -1。
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Service {
int id;
int power;
int value;
};
struct State {
long long value = 0;
int power = 0;
int count = 0;
// 比较函数,判断 newState 是否比当前 state 更优
bool is_better_than(const State& other) const {
if (value > other.value) return true;
if (value == other.value) {
if (power < other.power) return true;
if (power == other.power) {
if (count < other.count) return true;
}
}
return false;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin >> n;
int c;
cin >> c;
vector<Service> services(n);
for (int i = 0; i < n; ++i) {
services[i].id = i + 1;
cin >> services[i].power >> services[i].value;
}
vector<vector<State>> dp(n + 1, vector<State>(c + 1));
for (int i = 1; i <= n; ++i) {
int p = services[i - 1].power;
int v = services[i - 1].value;
for (int j = 0; j <= c; ++j) {
// 默认不选第i个服务
dp[i][j] = dp[i - 1][j];
// 尝试选择第i个服务
if (j >= p) {
State prev_state = dp[i - 1][j - p];
State new_state;
new_state.value = prev_state.value + v;
new_state.power = prev_state.power + p;
new_state.count = prev_state.count + 1;
if (new_state.is_better_than(dp[i][j])) {
dp[i][j] = new_state;
}
}
}
}
// 在所有可能的总功耗中找到最优解
State best_state = dp[n][0];
int final_power = 0;
for (int j = 1; j <= c; ++j) {
if (dp[n][j].is_better_than(best_state)) {
best_state = dp[n][j];
final_power = j;
}
}
if (best_state.value == 0) {
cout << -1 << endl;
return 0;
}
// 回溯找到选择的服务
vector<int> result_ids;
int current_power = final_power;
for (int i = n; i > 0 && current_power > 0; --i) {
// 比较状态对象需要比较所有成员
bool is_equal = (dp[i][current_power].value == dp[i - 1][current_power].value &&
dp[i][current_power].power == dp[i - 1][current_power].power &&
dp[i][current_power].count == dp[i - 1][current_power].count);
if (!is_equal) {
result_ids.push_back(services[i - 1].id);
current_power -= services[i - 1].power;
}
}
sort(result_ids.begin(), result_ids.end());
for (size_t i = 0; i < result_ids.size(); ++i) {
cout << result_ids[i] << (i == result_ids.size() - 1 ? "" : " ");
}
cout << endl;
return 0;
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
class Service {
int id;
int power;
int value;
public Service(int id, int power, int value) {
this.id = id;
this.power = power;
this.value = value;
}
}
class State {
long value = 0;
int power = 0;
int count = 0;
// 复制构造函数
public State(State other) {
this.value = other.value;
this.power = other.power;
this.count = other.count;
}
public State() {}
// 判断 this 是否比 other 更优
public boolean isBetterThan(State other) {
if (this.value > other.value) return true;
if (this.value == other.value) {
if (this.power < other.power) return true;
if (this.power == other.power) {
if (this.count < other.count) return true;
}
}
return false;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
State state = (State) obj;
return value == state.value && power == state.power && count == state.count;
}
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int c = sc.nextInt();
Service[] services = new Service[n];
for (int i = 0; i < n; i++) {
services[i] = new Service(i + 1, sc.nextInt(), sc.nextInt());
}
State[][] dp = new State[n + 1][c + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= c; j++) {
dp[i][j] = new State();
}
}
for (int i = 1; i <= n; i++) {
int p = services[i - 1].power;
int v = services[i - 1].value;
for (int j = 0; j <= c; j++) {
// 不选第i个服务
dp[i][j] = new State(dp[i - 1][j]);
// 尝试选第i个服务
if (j >= p) {
State prevState = dp[i - 1][j - p];
State newState = new State();
newState.value = prevState.value + v;
newState.power = prevState.power + p;
newState.count = prevState.count + 1;
if (newState.isBetterThan(dp[i][j])) {
dp[i][j] = newState;
}
}
}
}
State bestState = dp[n][0];
int finalPower = 0;
for (int j = 1; j <= c; j++) {
if (dp[n][j].isBetterThan(bestState)) {
bestState = dp[n][j];
finalPower = j;
}
}
if (bestState.value == 0) {
System.out.println(-1);
return;
}
List<Integer> resultIds = new ArrayList<>();
int currentPower = finalPower;
for (int i = n; i > 0 && currentPower > 0; i--) {
if (!dp[i][currentPower].equals(dp[i - 1][currentPower])) {
resultIds.add(services[i - 1].id);
currentPower -= services[i - 1].power;
}
}
Collections.sort(resultIds);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < resultIds.size(); i++) {
sb.append(resultIds.get(i));
if (i < resultIds.size() - 1) {
sb.append(" ");
}
}
System.out.println(sb.toString());
}
}
import sys
class State:
def __init__(self, value=0, power=0, count=0):
self.value = value
self.power = power
self.count = count
def is_better(s1, s2):
"""判断 s1 是否比 s2 更优"""
if s1.value > s2.value:
return True
if s1.value == s2.value:
if s1.power < s2.power:
return True
if s1.power == s2.power:
if s1.count < s2.count:
return True
return False
def is_equal(s1, s2):
return s1.value == s2.value and s1.power == s2.power and s1.count == s2.count
def solve():
lines = sys.stdin.readlines()
if not lines:
return
n = int(lines[0])
c = int(lines[1])
services = []
for i in range(n):
p, v = map(int, lines[2 + i].split())
services.append({'id': i + 1, 'power': p, 'value': v})
dp = [[State() for _ in range(c + 1)] for _ in range(n + 1)]
for i in range(1, n + 1):
service = services[i - 1]
p, v = service['power'], service['value']
for j in range(c + 1):
# 不选
dp[i][j] = dp[i-1][j]
# 选
if j >= p:
prev_state = dp[i-1][j-p]
new_state = State(
value=prev_state.value + v,
power=prev_state.power + p,
count=prev_state.count + 1
)
if is_better(new_state, dp[i][j]):
dp[i][j] = new_state
best_state = State()
final_power = 0
for j in range(c + 1):
if is_better(dp[n][j], best_state):
best_state = dp[n][j]
final_power = j
if best_state.value == 0:
print(-1)
return
result_ids = []
current_power = final_power
for i in range(n, 0, -1):
if not is_equal(dp[i][current_power], dp[i-1][current_power]):
service = services[i - 1]
result_ids.append(service['id'])
current_power -= service['power']
result_ids.sort()
print(' '.join(map(str, result_ids)))
solve()
算法及复杂度
- 算法: 动态规划 (0/1 背包变种) + 回溯
- 时间复杂度:
。
- 填充 DP 表格需要两层循环,遍历所有服务和所有功耗预算,因此复杂度为
。
- 回溯过程最多遍历
个服务,复杂度为
。
- 总体时间复杂度由 DP 部分主导。
- 填充 DP 表格需要两层循环,遍历所有服务和所有功耗预算,因此复杂度为
- 空间复杂度:
。
- 需要一个二维 DP 表来存储每个子问题的状态,空间大小为
。
- 需要一个二维 DP 表来存储每个子问题的状态,空间大小为

