20191110 csp-s模拟T1(dp)

T1 排兵布阵(arrange)

(WOJ4806)
【题目背景】
X X X是生活在 9102 9102 9102年的一位高二的 O I e r OIer OIer,也是本套模拟题的主角。
这是一件往事了. . . . . .
X X X正在玩一款排兵布阵的游戏,这时候小 R R R突然来找小 X X X出去陪她玩。
于是小 X X X把这个游戏丢给了你,希望你能帮他找到最优的策略,防止自己的不知道多少连胜断掉。
【题目描述】
在游戏中有 n n n座城堡,每局对战由两名玩家来争夺这些城堡。每名玩家有 m m m名士兵,可以向第i 座城堡派遣 a i a_i ai名士兵去争夺这个城堡,使得总士兵数不超过 m m m
如果一名玩家向第 i i i座城堡派遣的士兵数严格大于对手派遣士兵数的两倍,那么这名玩家就占领了这座城堡,获得 i i i分。
现在你即将和其他 s s s名玩家两两对战,这 s s s场对决的派遣士兵方案必须相同。小 X X X通过某些途径得知了其他 s s s名玩家即将使用的策略并告诉了你,你应该使用某种策略策略来最大化自己的总分。
由于方案可能不唯一,你只需要输出你能获得的总分的最大值。
【输入格式】
从文件 a r r a n g e . i n arrange.in arrange.in中读入数据。
输入第一行包含三个正整数 s , n , m s,n,m s,n,m,分别表示除了小 X X X以外的玩家人数、城堡数和每名玩家拥有的士兵数。
接下来 s s s行,每行 n n n个非负整数,表示一名玩家的策略,其中第 i i i个数 a i a_i ai表示这名玩家向第 i i i座城堡派遣的士兵数。
【输出格式】
输出到文件 a r r a n g e . o u t arrange.out arrange.out中。
输出一行一个非负整数,表示你能获得的最大得分。
【样例1输入】
1 3 10
2 2 6
【样例1输出】
3
【样例1解释】
一种最佳策略为向第 1 1 1座城堡和第 2 2 2座城堡各派遣 5 5 5名士兵。
【样例2输入】
2 3 10
2 2 6
0 0 0
【样例2输出】
8
【样例2解释】
一种最佳策略为向第 1 1 1座城堡派遣 2 2 2名士兵,向第 2 2 2座城堡派遣5名士兵,向第3座城堡派遣 1 1 1名士兵。
【样例3】
见选手目录下的arrange/arrange3.in与arrange/arrange3.ans。
【子任务】
对于100%的数据,保证 1 ≤ s ≤ 100 , 1 ≤ n ≤ 100 , 1 ≤ m ≤ 2 × 1 0 4 1\le s\le 100,1\le n\le 100,1\le m\le 2\times 10^4 1s100,1n100,1m2×104
对于每名玩家, a i < 0 , ∑ i = 1 n a i ≤ m a_i\lt 0,\sum^n_{i=1} a_i\le m ai<0,i=1naim
【提示】
请注意时间复杂度的常数因子对程序实际运行效率的影响。

思路:
首先想到 O ( n m 2 ) O(nm^2) Onm2 d p dp dp
d p [ i ] [ j ] dp[i][j] dp[i][j]表示 d p dp dp到第 i i i座城堡,剩余 j j j人时的最大得分。
一重循环枚举 i i i,一重循环枚举在这座城堡投放的人数,一重循环进行状态转移。
状态转移方程:
设在 i i i城堡投放 k k k人,有 d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − 1 ] [ j + k ] ) dp[i][j]=max(dp[i][j],dp[i-1][j+k]) dp[i][j]=max(dp[i][j],dp[i1][j+k])

时间肯定要爆……

发现只有在这座城堡投放人数为某一方敌方在这座城堡投放人数的二倍加一时,对答案有影响。
所以想到将敌方在这座城堡投放人数排序,从小到大转移。
注意有可能不在这一座城堡投放人数!
时间复杂度: O ( s n m ) O(snm) O(snm)
卡过……

代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register

inline char ch(){
	static char buf[1000000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}

inline int in{
	int s=0,f=1;char x;
	for(x=ch();!isdigit(x);x=ch())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=ch())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const int A=105;
const int B=2e4+5;
int s,n,m;
int a[A][A],b[A][A];
int all;
int dp[A][B],p;
int ans;

signed main(){
//	freopen("arrange.in","r",stdin);
//	freopen("arrange.out","w",stdout);
	s=in,n=in,m=in;
	for(re int i=1;i<=s;++i)
		for(re int j=1;j<=n;++j)
			a[i][j]=b[j][i]=in;
	for(re int i=1;i<=n;++i)
		sort(b[i]+1,b[i]+1+s);
	for(re int i=1;i<=n;++i){
		all+=b[i][s]*2+1;
		for(re int j=0;j<=s;++j){
			p=(j==0)?0:2*b[i][j]+1;
			for(re int k=m;k>=p;--k){
				if(k<m-all)    break;
				dp[i][k-p]=max(dp[i][k-p],dp[i-1][k]+i*j);
			}
		}
	}
	for(re int i=0;i<=m;++i)
		ans=max(ans,dp[n][i]);
	printf("%d",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值