题目

每块电池有两个参数,额定电压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;
}

posted on 2025-12-29 22:42  快乐的乙炔  阅读(0)  评论(0)    收藏  举报  来源