SDUT网络赛

来源:互联网 发布:铁路计算坐标数据库 编辑:程序博客网 时间:2024/05/21 03:19

只写三个题,一个是飞行棋(期望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 100010bool 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;}}



原创粉丝点击