洛谷 P5322 [BJOI2019] 排兵布阵 题解

题目链接

洛谷 P5322 [BJOI2019] 排兵布阵

思路分析

首先,假设我们已经定好了策略,对于第 \(i\) 个城堡出兵 \(b_i\),第 \(j\) 个玩家出兵 \(a_{i,j}\)。那么,我们发现,如果 \(b_i>2\times a_{i,j}\),且 \(a_{i,j}>a_{i,k}\),则有 \(b_i>2\times a_{i,k}\),即如果当前兵力可以攻下某一玩家,则所有派兵比这一玩家少的都能被攻占。

所以,我们先对于第 \(i\) 个城堡所有玩家的出兵数量从小到大排序。贪心地考虑,如果我们要攻下某一玩家,则只需要 \(2\times a_{i,j}+1\) 即可,再多了也没用。所以,对于城堡 \(i\),除了所有 \(2\times a_{i,j}+1\) 的兵力,其他都不用考虑。

我们把这 \(s\) 种兵力看成 \(s\) 个物品从小到大排列,每个物品代价为所需兵力,价值为可获收益。结合黑体性质,对于第 \(k\) 个物品,收益即为能攻下的玩家人数 \(k-1\) 乘上城堡编号 \(i\)。在每一个城堡 \(i\) 中都进行 0-1 背包,再整体进行背包即可。不难发现这即是分组背包,时间复杂度 \(O(nms)\)

代码呈现

#include<bits/stdc++.h>
using namespace std;

const int N=105,M=2e4+10;
int s,n,m;
int a[N][N],dp[M];

int main(){
    scanf("%d%d%d",&s,&n,&m);
    for (int i=1;i<=s;++i){
        for (int j=1;j<=n;++j) scanf("%d",&a[j][i]);
    }
    for (int i=1;i<=n;++i) sort(a[i]+1,a[i]+s+1);
    for (int i=1;i<=n;++i){
        for (int j=m;j>a[i][1]*2;--j){
            for (int k=1;k<=s;++k){
                if (j>a[i][k]*2) dp[j]=max(dp[j],dp[j-a[i][k]*2-1]+i*k);
            }
        }
    }
    int ans=0;
    for (int i=1;i<=m;++i) ans=max(ans,dp[i]);
    printf("%d",ans);
    return 0;
}
posted @ 2026-05-04 12:19  CodingJuRuo  阅读(7)  评论(0)    收藏  举报