【状态压缩DP】电子竞技

题目描述有点纠结,那个有关L的限制


电子竞技
【问题描述】
  每年的北京信息学冬令营上,同学们都会在课余开展丰富多彩的电子竞
技活动。经过一年又一年的实战,同学们对彼此的实力已经非常熟悉了,以
至于产生出了一套实力 –  对战评分系统。
  这套系统把每个人的实力量化为一个整数。并且对于两个实力分别为
B A R R , 的人A 和 B,这套系统把他们对战的通常的结果也量化为一个关于他
们实力值的实数分数:
400
400
10 1
1
10 1
1
B A
A B
R R B
R R A
E
E






 
其中 B A E E , 分别为 A 和 B 在对战中通常得到的对战分数。例如,若
2611 , 2432   B A R R ,则在 A 和 B 的对战中 A 取得的对战分数应该为
9459 0.26300523
10 1
1
400
179



不过,今年冬令营的情况与以往略有不同,同学们分成了人数相等的两
队展开了团体比赛。每队的老大给自己的队员排定一个顺序,然后两队按顺
序第一个人互相对战,第二个人互相对战,以此类推。对于团体比赛中对战
的每一对选手,我们都可以根据他们的实力值计算出他们分别获得的对战分
数。而两个团队在这样一种比赛顺序下的团队对战分数就是团队每一位选手
在这个顺序下获得的对战分数和。
为了公平竞赛,同学们约定了一个整数 L,并规定一个团队中实力为 x
的选手必须排在所有实力严格小于 x – L的选手之前。例如若 L = 100,实力
为 2437 的一名选手必须排在本团队实力为 2336 的选手之前,但是却不一定
排在本团队实力为 2337 的选手之前。
你作为一支队伍的老大,运用了种种不正当手段搞来了另外一直队伍排
好的队员顺序。并且你也了解自己队伍和对方队伍每一个人的实力值。你现
在的任务就是想方设法安排本队人员的顺序,使得你的团队在对战中获得的
分数最大。
 
【输入文件】
输入文件的第一行包含一个整数N,代表每队的人数。
接下来的N行,每行一个整数,表示你的每个队员的能力值。
接下来的N行,每行一个整数,依次表示对方团队的每个队员的能力值。
最后一行包含一个整数,代表 L。



【输出文件】
输出文件仅包含一个实数,表示你最大可能获得的分数。答案四舍五入保留
到小数点后六位
 
【样例输入】
4
2239
2412
2399
2267
2534
2429
2340
2389
100
 
【样例输出】
1.483574
 
【样例说明】
  根据题目中的规定,可能的安排顺序只可能有四种,它们能获得的分数如下
排列  分数
2399 2412 2239 2267  1.48040986
2399 2412 2267 2239  1.48357361
2412 2399 2239 2267  1.47815348
2412 2399 2267 2239  1.48131723
注意,如果没有 L的限制,排列 2239 2267 2399 2412 可以得到更多的分数
但是,由于2239 和 2267不允许2399 和 2412 之前出现,这是不可能的。
 
【数据规模】
对于100%的数据,有 1 ≤ N ≤ 20 成立。
对于100%的数据,每个人的能力值都在 1500 到 3000 之间。
对于100%的数据,有 0 ≤ L ≤ 1500 成立。
对于100%的数据,你的对手队伍排列好的顺序中的第 i 名队员的能力值与
L的和大于等于对手队伍中排在他之后的任意一名队员的能力值。
 
 
 


讲过了之后朴素的就很简单了。枚举阶段,枚举上一状态,枚举这一状态,判断是否可行,更新f值。

仔细讲讲优化:

1、预处理出可行状态:Stata[i][j]表示有i个1,第j个状态是多少。数1个个数可以用二分方法。因为若不考虑L的影响则不会有无效状态,所以很高效。

※2、得分必须要预处理,很重要。

3、一个很有效的优化,假设X是已经存在的点,S是当前新的点,即S是在X后面。若S<X-L则满足条件,如果S>X-L则不满足条件。

由此可知如果S>Xmin-L则一定不满足条件。因此我们按降序将R排序,需要判断的时候,得到pre的最高位的1的位置,即已有的最小R。

4、3中的排序用冒泡要快点,因为不可能有相等的,所以可以直接用异或三次的方法来交换。

5、3中取最高位1的位置,也可以用二分的方法。

※6、因为不同的阶段的方案集合相互没有交集,因此可以就地滚动,空间和时间都有优化。


2和6比较重要,其他的优化效果不明显

一直不知道哪里超时,优化了各种东西,但是忘了最关键的地方,就是两两比赛的得分没有预处理。



#include <cstdio>
#include <cmath>
#include <string>
#define MAX(a,b) ((a)>(b)?(a):(b))
#define CALC(a,b) (1.0/(1.0+pow(10.0,(double(b)-double(a))/400.0 ) ) )

int N;int L;
int R1[30];
int R2[30];
int Stata[21][1048586];
double EXP[22][22];
double f[1048586];

inline int Count1(int x)
{
	x=(x&0x55555555)+((x>>1)&0x55555555); //1
	x=(x&0x33333333)+((x>>2)&0x33333333);//2
	x=(x&0x0f0f0f0f)+((x>>4)&0x0f0f0f0f); //3
	x=(x&0x00ff00ff)+((x>>8)&0x00ff00ff); //4
	x=(x&0x0000ffff)+((x>>16)&0x0000ffff);//5
	return x;
}

inline int getint()
{  
    int ret = 0;  
    char tmp;  
    while (!isdigit(tmp = getchar()));  
    do {  
        ret = (ret << 3)+(ret << 1) + tmp - '0';  
    } while (isdigit(tmp = getchar()));  
    return ret;
}  

int main()
{
	freopen("compete.in","r",stdin);
	freopen("compete.out","w",stdout);

	N = getint();
	for (int i=1;i<N+1;i++)
		R1[i] = getint();
	for (int i=1;i<N+1;i++)
		R2[i] = getint();
	
	for (int i=2;i<N+1;i++)
		for (int j=i-1;j>0;j--)
		{
			if (R1[j]<R1[j+1])
			{
				R1[j] ^= R1[j+1];
				R1[j+1] ^= R1[j];
				R1[j] ^= R1[j+1];
			}
			else break;
		}
	L = getint();
	
	for (int i=0;i<(1<<N);i++)
	{	
		int n1 = Count1(i);
		Stata[n1][++Stata[n1][0]] = i;
	}
	
	for (int i=1;i<N+1;i++)
	{
		for (int j=1;j<N+1;j++)
		{
			EXP[i][j] = CALC(R1[i],R2[j]);
		}
	}
	for (int i=1;i<N+1;i++)
		for (int k=1;k<Stata[i-1][0]+1;k++)
		{
			int pre = Stata[i-1][k];
			for (int pos=0;pos<N;pos++)
			{
				int now = pre|(1<<pos);
				if (now == pre) continue;
				
				/*							*\
				找二进制数最高位的二分方法 
				\*							*/
				int pos2;
				{
					unsigned x = pre;
					int n=1;
				    if ((x>>16) == 0) {n = n+16; x = x<<16;}
				    if ((x>>24) == 0) {n = n+8; x = x<<8;}
				    if ((x>>28) == 0) {n = n+4; x = x<<4;}
				    if ((x>>30) == 0) {n = n+2; x = x<<2;}
				    n = n-(x>>31);
					pos2 = 31-n;
					if (x==0)
						pos2=-1;
				}

				if (pre && R1[pos2+1]+L<R1[pos+1])	continue;
				f[now] = MAX(f[now],f[pre]+EXP[pos+1][i]);
			}
	        }
	
	double ans = 0;
	for (int j=0;j<Stata[N][j]+1;j++)
		ans = MAX(ans,f[Stata[N][j]]);
	printf("%.6lf",ans);
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值