CDOJ 796 DAGE(Big Brother)

DAGE(Big Brother)

Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)

Our famous “DAGE”, God Kufeng, likes killing monsters. However, killing monsters is a tiring job, and might do harm to DAGE’s health.

Now there are n monsters, each monster has a strength value. Let’s say, before killing a monster with strength X , DAGE has a health point(HP) as V , then after killing, DAGE’s HP will become

Vnew=V&X

Here & means bitwise AND. For example, 5&4=4 , 5&2=0 .

At first, the health point(HP) of DAGE will be 2047 .

DAGE has a simple strategy, each time he just randomly choose a monster to kill, and kill that monster.

When DAGE’s HP becomes 0 , DAGE will feel tired and go home to sleep.

What is the expected number of monsters DAGE has killed when DAGE goes to bed? It is guaranteed that DAGE will go to bed at some moment.

Input

The first line contains a single number n , the number of the monsters. ( 1n50 )

The the second line comes with n numbers x1,x2,,xn , those are the strenght value of each monster. (0xi2047) .

Output

Output a single number in a line, the expected number of monsters DAGE has killed when he goes to bed, rounded to 3 digits after decimal point.

Sample input and output

Sample InputSample Output
3
0 1 2
1.667

Source

the 12th UESTC Programming Contest Preliminary

解析

概率DP

dp[i][j]表示杀了 i 个怪还剩 j 滴血的概率。转移的时候有个问题,不知道哪些怪被杀过。(同一个怪不能杀两次)

其实我不用知道哪些怪死了(因为它们的概率会叠加),我只用知道那个时候死了多少个怪。(如果看不懂这句话可以忽略它,看代码就会明白的)

有个关于 x[q]&j==j 的结论:
如果先前第a个怪被打过那么一定有x[a]&j==j
然而怪b满足x[b]&j==j却不一定说明怪b被打过

那么对于血量为 j 的时候,我统计一下哪些怪可能被杀过
for(int k=1;k<=N;k++)//血量为j时,最多可能打cnt个怪
      if((x[k]&j)==j) cnt++;
从dp[i][j]转移到dp[i+1][j]的可能性是 (cnt-i)/(N-i)
不满足的 (x[k]&j)==j 的转移就是概率就是1/(N-i)

注意&的优先级比==低。还有题目要求的的期望是血为0的期望,杀完了N个怪,血还没用完是不回家的。

这份代码得到张倩如的指导。



#include<cstdio>
#include<algorithm>

using namespace std;

int N,x[55];
double dp[55][2048];

void readdata()
{
	scanf("%d",&N);
	for(int i=1;i<=N;i++) scanf("%d",&x[i]);
}

void dynamic_planning()
{
	dp[0][2047]=1;
	for(int i=0;i<N;i++)//有i个怪被杀了
		for(int j=1;j<2048;j++)//当前血量
		{
			int cnt=0;
			for(int k=1;k<=N;k++)//血量为j时,最多可能打cnt个怪
				if((x[k]&j)==j) cnt++;
			if(i>cnt) continue;//当前的j不可能杀i个怪
			dp[i+1][j]+=(dp[i][j]*(cnt-i))/(N-i);//杀一个满足(x[k]&j)==j的怪
			for(int k=1;k<=N;k++)
				if((x[k]&j)!=j)//杀一个不满足(x[k]&j)==j的怪
					dp[i+1][x[k]&j]+=dp[i][j]/(N-i);
		}
}
void write()
{
	double ans=0;
	for(int i=1;i<=N;i++) ans+=dp[i][0]*i;
	printf("%.3f",ans);
}

int main()
{
	readdata();
	dynamic_planning();
	write();

	while(1);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值