1 条题解

  • 0
    @ 2025-5-4 13:59:01

    分析:

    该问题本质上是一个组合优化问题,需要将给定重量的石头组合成满足力矩平衡条件的悬挂结构,并最大化其宽度。关键约束是: 1.必须使用所有石头

    2.总宽度必须小于房间宽度

    3.悬挂结构需满足力矩平衡条件 n·a = m·b(n,m 为子结构重量,a,b 为到重心距离)

    算法核心原理

    状态表示:使用位掩码(二进制数)表示石头的子集,例如 110 表示选择第 1 和第 2 块石头(从 0 开始计数)sum[subset] 存储子集 subset 中所有石头的总重量tree[subset] 存储子集 subset 能形成的所有合法悬挂结构的左右端点坐标

    力矩平衡原理: 当左右子结构重量为 sum[left] 和 sum[right] 时,根据 sum[left]·a = sum[right]·b 且 a + b = 1(杆子长度为 1),可解得: 左子结构到重心距离 a = sum[right] / (sum[left] + sum[right]) 右子结构到重心距离 b = sum[left] / (sum[left] + sum[right])

    宽度计算:悬挂结构的宽度由左右端点坐标差决定,即 right - left。合并左右子结构时,需考虑坐标偏移:左子结构向右移动 a,右子结构向左移动 b

    算法步骤详解

    1.输入处理与初始化: 读取数据集数量 T 和每个数据集的房间宽度 r、石头数量 n 及重量 w[i] 预处理每个子集的总重量 sum[subset],通过位运算遍历所有可能的子集

    2.深度优先搜索(DFS)处理子集

    关键步骤解析

    1.子集枚举:使用 (subset - 1) & subset 高效枚举所有非空真子集,避免重复计算

    2.力矩平衡计算:根据左右子结构重量比,计算各自到重心的距离

    3.坐标合并:将左右子结构的坐标按距离偏移后,取边界最大值作为新结构的边界

    4.合法性检查:确保合并后的宽度(t.l + t.r)小于房间宽度 r

    5.结果计算: 遍历全集(所有石头)的所有合法悬挂结构,找到最大宽度

    c++代码:

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    using namespace std;
    const int N=6;
    double r,sum[1<<N];
    int n,w[N],T,vis[1<<N];
    struct node{
        double l,r;
        int ls,rs;
        node():l(0),r(0){}
    };
    vector<node> tree[1<<N];
    void dfs(int subset){//printf("dfs %d\n",subset);
        if(vis[subset]) return;
        vis[subset]=1;
        int child=0;
        for(int left=(subset-1)&subset;left;left=(left-1)&subset){
            child=1;
            int right=left^subset;
            dfs(left);dfs(right);
            
            double dl=sum[right]/sum[subset],dr=sum[left]/sum[subset];
            for(int i=0;i<tree[left].size();i++)
                for(int j=0;j<tree[right].size();j++){
                    node t;
                    t.l=max(tree[left][i].l+dl,tree[right][j].l-dr);
                    t.r=max(tree[left][i].r-dl,tree[right][j].r+dr);
                    if(t.l+t.r<r) tree[subset].push_back(t);
                }
        }
        if(!child) tree[subset].push_back(node());}
        int main(int argc, const char * argv[]) {
        scanf("%d",&T);
        while(T--){
            scanf("%lf%d",&r,&n);
            for(int i=0;i<n;i++) scanf("%d",&w[i]);
            for(int i=0;i<(1<<n);i++){
                sum[i]=vis[i]=0;
                tree[i].clear();
                for(int j=0;j<n;j++) if(i&(1<<j)) sum[i]+=w[j];
            }
            //for(int i=0;i<(1<<n);i++) printf("sum %d\n",sum[i]);
            int root=(1<<n)-1;
            dfs(root);
            
            double ans=-1;
            for(int i=0;i<tree[root].size();i++)
                ans=max(ans,tree[root][i].l+tree[root][i].r);
            printf("%.10f\n",ans);
        }
        return 0;
    }
    
    
    • 1

    信息

    ID
    1743
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    4
    已通过
    1
    上传者