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