「BJOI 2019」排兵布阵

传送门


problem

C 正在玩一款排兵布阵的游戏。在游戏中有 n n n 座城堡,每局对战由两名玩家来争夺这些城堡。每名玩家有 m m m 名士兵,可以向第 i i i 座城堡派遣 a i a_i ai 名士兵去争夺这个城堡,总士兵数不超过 m m m

如果一名玩家向第 i i i 座城堡派遣的士兵数严格大于对手派遣士兵数的两倍,那么这名玩家就占领了这座城堡,获得 i i i 分。

现在小 C 即将和其他 s s s 名玩家两两对战,这 s s s 场对决的派遣士兵方案必须相同。小 C 通过某些途径得知了其他 s s s 名玩家即将使用的策略,他想知道他应该使用什么策略来最大化自己的总分。

由于答案可能不唯一,你只需要输出小 C 总分的最大值。

数据范围: 1 ≤ s ≤ 100 1≤s≤100 1s100 1 ≤ n ≤ 100 1\le n \le 100 1n100 1 ≤ m ≤ 20000 1\le m \le 20000 1m20000


solution

这道题其实挺水的。

对于第 i i i 场比赛的第 j j j 个人,假设他派出了 a i , j a_{i,j} ai,j 的兵,那么可以花费 2 a i , j + 1 2a_{i,j}+1 2ai,j+1 的代价获得 i i i 的贡献。

由于有多个人,对于两个不同的人 j j j k k k,如果 a i , j < a i , k a_{i,j}ai,j<ai,k,那么花费 2 a i , k + 1 2a_{i,k}+1 2ai,k+1 的代价可以同时获得两个人的贡献。所以说我们要做一个前缀和,即统计用当前的代价能获得的总贡献。

于是,问题转换成了:有 n n n 组物品,每组有 s s s 个物品,每个物品有代价价值。现在要在总代价 ≤ m \le m m 的情况下,从每组中选出一个,使总价值最大。

这就是分组背包模板题啦。时间复杂度 O ( s n m ) O(snm) O(snm),不过常熟很小。


code

#include
using namespace std;
const int N=105,M=20005;
int s,n,m,a[N][N],v[N],w[N],val[M],f[M];
int main(){
	scanf("%d%d%d",&s,&n,&m);
	for(int j=1;j<=s;++j)
		for(int i=1;i<=n;++i)
			scanf("%d",&a[i][j]);
	for(int i=1;i<=n;++i){
		int tot=0,last=0;
		memset(val,0,sizeof(val));
		for(int j=1;j<=s;++j)  val[a[i][j]<<1|1]+=i;
		for(int j=1;j<=m;++j){
			if(!val[j])  continue;
			val[j]+=val[last],last=j,v[++tot]=j,w[tot]=val[j];
		}
		for(int j=m;j>=0;--j)
			for(int k=1;k<=tot;++k)
				if(j>=v[k])  f[j]=max(f[j],f[j-v[k]]+w[k]);
	}
	printf("%d\n",f[m]);
	return 0;
}

你可能感兴趣的:(#,背包问题)