SDUT网络赛

只写三个题,一个是飞行棋(期望DP),super prime(引申连续数字和等于一个数的问题)和Dota人王之战

一,飞行棋:

http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2503

思想如下:

首先明白什么叫数学期望。http://en.wikipedia.org/wiki/Mathematical_expectation

其次,这个题中一定要搞明白,限制条件是期望得分需要大于等于所给格子数,而输出的是期望的掷色子次数。在这个题中,对于1的期望值,就是剩余一个格子,掷色子的期望值。即,获胜所需的掷色子次数。

当格子数目是1时:只需要投一次色子,故期望为1;

当格子数目是2时:掷色子,2,3,4,5,6都可能出现,他们是绝对获胜的,所以期望是sigma(出现次数*频率),就是1,然后,如果掷到1,剩余一个格子,获胜期望是dp[1],出现概率是1*dp[1];期望是1+1/6*dp[1];

当格子数目是3时:3,4,5,6都可能出现,是绝对获胜的,他们的期望是1,然后,如果掷到1,剩余两个格子,获胜期望是dp[2],如果掷到2,剩余一个格子,获胜期望是dp[1];期望是1+1/6*(dp[1]+dp[2]);

...

当格子数目是6时:6可能出现,是绝对获胜的,他的期望是1,然后,如果掷到1,剩余五个格子,获胜的期望是dp[5],如果掷到2,剩余四个格子,获胜期望是dp[4],如果掷到3,剩余三个格子,获胜期望是dp[3],如果掷到4,剩余两个格子,获胜期望是dp[2],如果掷到5,剩余一个格子,获胜期望是dp[1];期望是1+1/6*(dp[1]+dp[2]+dp[3]+dp[4]+dp[5]);

...

当格子数是n(n>=7)时,没有一次获胜的可能,所以需要掷一次,期望首先+1,然后,如果掷到1,剩余n-1个格子,获胜的期望是dp[n-1],如果掷到2,剩余n-2个格子,获胜的期望是dp[n-2],如果掷到3,剩余n-3个格子,获胜的期望是dp[n-3],如果掷到4,剩余n-4个格子,获胜的期望是dp[n-4],如果掷到5,剩余n-5个格子,获胜的期望是dp[n-5],如果掷到6,剩余n-6个格子,获胜的期望是dp[n-6];期望1+1/6*(dp[n-1]+dp[n-2]+dp[n-3]+dp[n-4]+dp[n-5]);

...


故,综上述,不难得出以下程序:

#include <iostream>
#include <cstdio>

using namespace std;

double dp[10000];

int main()
{
	dp[1]=1.0;
	dp[2]=1.0/6*dp[1]+1;
	dp[3]=1.0/6*(dp[1]+dp[2])+1;
	dp[4]=1.0/6*(dp[1]+dp[2]+dp[3])+1;
	dp[5]=1.0/6*(dp[1]+dp[2]+dp[3]+dp[4])+1;
	dp[6]=1.0/6*(dp[1]+dp[2]+dp[3]+dp[4]+dp[5])+1;

	int t;
	cin>>t;

	while(t--)
	{
		int n;
		cin>>n;

		for(int i=7;i<=n;i++)
		{
			dp[i]=1.0/6*(dp[i-6]+dp[i-5]+dp[i-4]+dp[i-3]+dp[i-2]+dp[i-1])+1;
		}

		printf("%.4lf\n",dp[n]);
	}
}


二,super prime

http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2404

一直不明白为何错误。

但是其求连续子序列和是否等于某值的思想是可以借鉴的,一个双端队列的实现。详解请看程序:

#include <iostream>
#include <cmath>
#include <deque>
#include <numeric>
#include <functional>
#include <stdlib.h>

using namespace std;

#define Max 100010
bool prime[Max];
void IsPrime()
{
     prime[0]=prime[1]=0;prime[2]=1;
     for(int i=3;i<Max;i++)
        prime[i]=i%2==0?0:1;
     int t=(int)sqrt(Max*1.0);
     for(int i=3;i<=t;i++)
       if(prime[i])
         for(int j=i*2;j<Max;j+=i)
            prime[j]=0;
}
int main()
{
    IsPrime();
	int prime_real[10000];
	int j = 0;
	for(int i = 0 ; i < Max; i++)
	{
		if(prime[i])
		{
			prime_real[j++] = i;
		}
	}
	/*for(int i = 0 ; i < j; i++)
	{
		cout<<prime_real[i]<<' ';
	}
	system("pause");*/
	
    int n;
	cin>>n;
	for(int t = 1; t < n; t++)
	{
		int p;
		cin>>p;
		if(!prime[p])
		{
			cout<<"Case "<<t<<": no"<<endl;
			continue;
		}
		else
		{
			deque<int> a;
			a.push_back(prime_real[0]);
			for(int i = 1; i <= j; )
			{
				int sum = accumulate(a.begin(),a.end(),0);//计算当前的和
				if(sum < p)//如果和小于该定值,下一个数入队
				{
					a.push_back(prime_real[i++]);
				}
				else if(sum == p && a.size() != 1)//如果和等于该值,且不是它本身,输出yes并结束
				{
					cout<<"Case "<<t<<": yes"<<endl;
					break;
				}
				else if(sum > p && a.size() == 1)//如果和大于该值且队列大小是1,那么是不正确的,因为在维护和的过程中,和是一直在被不短加减的,如果加上这个大于它,说明不存在连续的,满足要求。
				{
					cout<<"Case "<<t<<": no"<<endl;
					break;
				}
				else//其他就属于加上当前的值,太大,所以要从队前弹出,保证连续性
				{
					a.pop_front();
				}
			}
		}
	}
}


三,人王之战

这个民间有个游戏,跟这个很相似,叫倒三十或抢三十,请移步:http://hi.baidu.com/macchinetta/item/7e737d09ad97f232a3332aa1

有了上述基础,不难写出如下代码:

#include <iostream>

using namespace std;

int main()
{
	int n,m;
	ios::sync_with_stdio(false);
	while(cin>>n>>m)
	{
		if(n%(m+1))
		{
			cout<<"A new star rise"<<endl;
		}
		else cout<<"Orz Dota God"<<endl;
	}
}



你可能感兴趣的:(ACM题解报告)