题目
每块电池有两个参数,额定电压v和标称容量c。
使用多个电池串联可以形成module,提高整体的电压;使用多个电池并联也可以形成module,提高整体的容量。多个串联形式的module可以组成pack,多个并联形式的module也可以组成pack,但是串联形式和并联形式的module不能组成pack。
module(pack)的电压和容量计算方式是:
1. 串联n个电池(module)电压为vi,容量为ci,总容量等于最小的一个ci,总电压等于所有vi之和。
2. 并联n个电池(module)电压为vi,容量为ci,总电压等于最小的一个vi,总容量等于所有ci之和。
给定N(正整数,N<12)块电池,每块电池的电压vj(正整数,vj<1e9)和容量cj(正整数,cj<1e9),以及一个目标电压下界(正整数,V<1e9)。
要求使用全部N块电池,恰好组成一个合法的pack,使得:
1. pack的总电压不低于V;
2. 在满足条件1的前提下,pack的总容量尽可能大;
3. 在满足条件1和2的前提下,所用的module数量尽可能少。
输出此时的module数量m和每个module中的电池数量k。
输出时,将两个数按升序排列。
输入格式
第一行:N V。
接下来N行:每行两个整数ci vi,表示第i块电池的容量和电压。
输出格式
一行,两个整数:min(m, k)和max(m, k)。
数据保证:至少存在一种合法方案满足电压要求。
参考解法
枚举因子对->固定首元素剪枝划分子集->算方案A、B(先串后并或者反过来)考虑电压约束->比较容量->取模块(module)数最少的解。
Python:
from itertools import combinations
def solve():
N, V = map(int, input().split())
batteries = []
for _ in range(N):
c, v = map(int, input().split())
batteries.append((c, v))
best_capacity = -1
best_m, best_k = None, None
# 枚举所有(m, k)对满足 m*k=N
for m in range(1, N + 1):
if N % m != 0:
continue
k = N // m
# 生成所有无重复分割(固定第一个元素避免顺序重复)
for partition in gen_partitions(list(range(N)), k):
# 尝试方案A:模块内串,模块间并
result_A = try_scheme_A(batteries, partition, V)
if result_A is not None:
cap_A, vol_A = result_A
if cap_A > best_capacity or (cap_A == best_capacity and m < best_m):
best_capacity = cap_A
best_m, best_k = m, k
# 尝试方案B:模块内并,模块间串
result_B = try_scheme_B(batteries, partition, V)
if result_B is not None:
cap_B, vol_B = result_B
if cap_B > best_capacity or (cap_B == best_capacity and m < best_m):
best_capacity = cap_B
best_m, best_k = m, k
# 输出:小的数在前
print(min(best_m, best_k), max(best_m, best_k))
def gen_partitions(indices, k):
"""
生成将indices分成若干个大小为k的组的所有方式。
通过固定第一个未分配的元素来避免模块顺序重复。
"""
if not indices:
yield []
return
# 固定第一个元素必须在当前组中,避免对称重复
first = indices[0]
remaining_indices = indices[1:]
# 从剩余元素中选择 k-1 个加入当前组
for rest_in_group in combinations(remaining_indices, k - 1):
group = [first] + list(rest_in_group)
remaining = [x for x in remaining_indices if x not in rest_in_group]
# 递归分割剩余元素
for p in gen_partitions(remaining, k):
yield [group] + p
def try_scheme_A(batteries, partition, V):
"""
方案A:模块内串联,模块间并联
每个模块(内部串联):
- 容量 = min(各电池容量)
- 电压 = sum(各电池电压)
整个Pack(模块间并联):
- 容量 = sum(各模块容量)
- 电压 = min(各模块电压)
"""
modules = []
for group_indices in partition:
group_batteries = [batteries[i] for i in group_indices]
# 模块属性(内部串联)
module_cap = min(c for c, v in group_batteries)
module_vol = sum(v for c, v in group_batteries)
modules.append((module_cap, module_vol))
# Pack属性(模块间并联)
pack_voltage = min(vol for cap, vol in modules)
pack_capacity = sum(cap for cap, vol in modules)
if pack_voltage >= V:
return (pack_capacity, pack_voltage)
else:
return None
def try_scheme_B(batteries, partition, V):
"""
方案B:模块内并联,模块间串联
每个模块(内部并联):
- 容量 = sum(各电池容量)
- 电压 = min(各电池电压)
整个Pack(模块间串联):
- 容量 = min(各模块容量)
- 电压 = sum(各模块电压)
"""
modules = []
for group_indices in partition:
group_batteries = [batteries[i] for i in group_indices]
# 模块属性(内部并联)
module_voltage = min(v for c, v in group_batteries)
module_capacity = sum(c for c, v in group_batteries)
modules.append((module_capacity, module_voltage))
# Pack属性(模块间串联)
pack_voltage = sum(vol for cap, vol in modules)
pack_capacity = min(cap for cap, vol in modules)
if pack_voltage >= V:
return (pack_capacity, pack_voltage)
else:
return None
if __name__ == "__main__":
solve()
C++:
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
#include <cctype>
#include <numeric> // for iota
using namespace std;
using ll = long long;
/* ---------- 1. 无重复划分生成器 ---------- */
void genPartitions(const vector<int> &idx, int k,
vector<vector<int>> &cur,
vector<vector<vector<int>>> &out)
{
if (idx.empty())
{
out.push_back(cur);
return;
}
int first = idx[0]; // 固定首元素
vector<int> rest(idx.begin() + 1, idx.end());
// 选 k-1 个元素与 first 组成当前子集
vector<int> mask(rest.size(), 0);
fill(mask.begin(), mask.begin() + k - 1, 1); // 1 表示被选
do
{
vector<int> group = {first};
vector<int> remaining;
for (size_t i = 0; i < rest.size(); ++i)
{
if (mask[i])
group.push_back(rest[i]);
else
remaining.push_back(rest[i]);
}
cur.push_back(group);
genPartitions(remaining, k, cur, out);
cur.pop_back();
} while (prev_permutation(mask.begin(), mask.end()));
}
/* ---------- 2. 方案 A(内部串,外部并) ---------- */
pair<ll, ll> evalA(const vector<pair<ll, ll>> &bat,
const vector<vector<int>> &part, ll V)
{
ll packCap = 0, packVolt = LLONG_MAX;
for (auto &g : part)
{
ll minCap = LLONG_MAX, sumVolt = 0;
for (int id : g)
{
minCap = min(minCap, bat[id].first);
sumVolt += bat[id].second;
}
packCap += minCap;
packVolt = min(packVolt, sumVolt);
}
if (packVolt >= V)
return {packCap, packVolt};
return {-1, -1};
}
/* ---------- 3. 方案 B(内部并,外部串) ---------- */
pair<ll, ll> evalB(const vector<pair<ll, ll>> &bat,
const vector<vector<int>> &part, ll V)
{
ll packVolt = 0, packCap = LLONG_MAX;
for (auto &g : part)
{
ll sumCap = 0, minVolt = LLONG_MAX;
for (int id : g)
{
sumCap += bat[id].first;
minVolt = min(minVolt, bat[id].second);
}
packVolt += minVolt;
packCap = min(packCap, sumCap);
}
if (packVolt >= V)
return {packCap, packVolt};
return {-1, -1};
}
/* ---------- 4. 主函数 ---------- */
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N;
ll V;
if (!(cin >> N >> V))
return 0;
vector<pair<ll, ll>> bat(N); // (capacity, voltage)
for (int i = 0; i < N; ++i)
cin >> bat[i].first >> bat[i].second;
ll bestCap = -1;
int bestM = -1, bestK = -1;
// 枚举所有因子对 (m, k) 使得 m*k = N
for (int m = 1; m <= N; ++m)
{
if (N % m)
continue;
int k = N / m;
// 生成所有无序划分
vector<vector<vector<int>>> parts;
vector<int> allIdx(N);
iota(allIdx.begin(), allIdx.end(), 0);
vector<vector<int>> cur;
genPartitions(allIdx, k, cur, parts);
// 对每个划分分别评估两种方案
for (auto &part : parts)
{
auto [capA, volA] = evalA(bat, part, V);
if (capA != -1 && (capA > bestCap | (capA == bestCap && m < bestM)))
{
bestCap = capA;
bestM = m;
bestK = k;
}
auto [capB, volB] = evalB(bat, part, V);
if (capB != -1 && (capB > bestCap | (capB == bestCap && m < bestM)))
{
bestCap = capB;
bestM = m;
bestK = k;
}
}
}
cout << min(bestM, bestK) << ' ' << max(bestM, bestK) << '\n';
return 0;
}
浙公网安备 33010602011771号