uva 10328 - Coin Toss 投硬币(dp递推,大数)

 

题意:抛出n次硬币(有顺序),求至少k个以上的连续正面的情况的种数。

思路:转换成求抛n个硬币,至多k-1个连续的情况种数,用所有可能出现的情况种数减去至多k-1个的情况,就得到答案了。此题涉及大数加减。

分析:

(1)假设n=k,那么只有一种情况。

(2)假设n=k+1,那么有3种情况,包含k个的两种,k+1个的一种。

(3)假设k=1,那么只有无正面这一种的情况不能被考虑,其他都能算,那么就是(1<<n)-1种。(n个硬币有2^n种结果)

(4)其他情况考虑递推。先把问题的规模降低,最小就是1了,再逐渐增大,每次规模增加1,直到n为止。用dp[n+1]这么大的数组就够了,而dp[n]表示从第1个硬币到第n个硬币中最多出现k-1个连续正面的情况种数。到这步,可以直接将拿到的k自减一,假设结果为t。那么问题转成求抛第i个硬币时,它和它前面不能超过t个正面。

(5)基于上面第4步的分析。可以看出,在抛第t个之前(包括第t个),最多就出现t个正面,不可能超过抛的次数的。那么dp[0~k]可以一次性初始化为1<<i,也就是有这么多抛法其结果都不会超过t个正面啦。

(6)考虑在抛第k+1个的情况,顶多也就一种不合法,也就是全面都是正面,那么种数就是(1<<k+1)-1。

(7)考虑在抛大于第k+2的情况,不妨设为dp[k+2]=dp[k+1]*2,但是这里面包含了大于t个的情况,只可能出现这样的情况:第k+2个刚好抛到正面,如果其前面与其连续的刚好有t个正面,那么就会造成连续t+1个正面,非法!但是不可能出现多于t+2个连续的可能,如果多于t+2,那么假设当前为正面,其前面必须有t+1个连续,而dp[k+1]中不会记录有非法的情况,所以不考虑。那么出现与第k+2个刚好连起来是t+1个连续的情况会有多少种?(k+2)-t-1这个位置肯定是反面的,不然就会造成连续t+2个了,那么在k+2-t-2及其之前所有不超过t个连续的情况种数就是出现“与k相连t+1个正面”的情况种数,减去这个数量即可。

总之,对于i大于k+2的,dp[i]=dp[i-1]*2-dp[i-t-2]。推到dp[n]时,所有可能都出来了。用所有可能数减去此数:答案= (1<<n)-dp[n]。

 

  1 #include <iostream>
  2 #include <vector>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <string>
  6 #include <algorithm>
  7 using namespace std;
  8 int n, k;
  9 long long a[70];
 10 vector<string> ans;
 11 vector<string> pre;
 12 
 13 bool comp(string num1,string num2)
 14 {
 15     int leng=num1.length(),i;
 16     for(i=0;i<leng;i++){if(num1[i]!='0')break;}
 17     num1=num1.substr(i,leng);
 18     if(num1.length()==0)num1="0";
 19 
 20     leng=num2.length();
 21     for(i=0;i<leng;i++){if(num2[i]!='0')break;}
 22     num2=num2.substr(i,leng);
 23     if(num2.length()==0)num2="0";
 24 
 25     if(num1.length()>num2.length())return true;
 26     else if(num1.length()==num2.length())
 27     {
 28         if(num1>=num2)return true;
 29         else return false;
 30     }
 31     else return false;
 32 }
 33 string _minus(string num1,string num2)
 34 {
 35     string result;
 36     if(comp(num2,num1)){string ss=num1;num1=num2;num2=ss;}
 37     reverse(num1.begin(),num1.end());
 38     reverse(num2.begin(),num2.end());
 39 
 40     result="";
 41 
 42     int i;
 43     for(i=0;i<int(num1.length())&&i<int(num2.length());i++)
 44     {
 45         char c=num1[i]-num2[i]+48;
 46         result=result+c;
 47     }
 48     if(i<int(num1.length()))
 49         for(;i<int(num1.length());i++)
 50             result=result+num1[i];
 51 
 52     int jiewei=0;
 53     for(i=0;i<int(result.length());i++)
 54     {
 55         int zhi=result[i]-48+jiewei;
 56         if(zhi<0)   {zhi=zhi+10;jiewei=-1;}
 57         else    jiewei=0;
 58         result[i]=(char)(zhi+48);
 59     }
 60 
 61     for(i=result.length()-1;i>=0;i--)
 62         if(result[i]!='0')
 63             break;
 64 
 65     result=result.substr(0,i+1);
 66     reverse(result.begin(),result.end());
 67     if(result.length()==0)result="0";
 68     return result;
 69 }
 70 string sum(string s1,string s2)
 71 {
 72     if(s1.length()<s2.length())
 73     {
 74         string temp=s1;
 75         s1=s2;
 76         s2=temp;
 77     }
 78     int i,j;
 79     for(i=s1.length()-1,j=s2.length()-1;i>=0;i--,j--)
 80     {
 81         s1[i]=char(s1[i]+(j>=0?s2[j]-'0':0));   //注意细节
 82         if(s1[i]-'0'>=10)
 83         {
 84             s1[i]=char((s1[i]-'0')%10+'0');
 85             if(i) s1[i-1]++;
 86             else s1='1'+s1;
 87         }
 88     }
 89     return s1;
 90 }
 91 
 92 void cal64()
 93 {
 94     ans[0]="1";
 95     for(int i=1; i<=k; i++)
 96         ans[i]=pre[i];
 97     string tmp= sum(ans[k], ans[k]);
 98     ans[k+1]=_minus(tmp,"1");
 99 
100     for(int i=k+2; i<=n; i++)
101     {
102         tmp=sum( ans[i-1], ans[i-1] );
103         ans[i]=_minus(tmp,ans[i-k-2]);
104     }
105     cout<<_minus(pre[n], ans[n])<<endl;
106 
107 }
108 
109 void cal63()
110 {
111     a[0]=1;
112     for(int i=1; i<=k; i++)      //小于k的情况,直接2^k
113         a[i]=a[i-1]+a[i-1];
114 
115     a[k+1]=a[k]+a[k]-1;
116     for(int i=k+2; i<=n; i++)
117         a[i] = a[i-1]-a[i-k-2]+a[i-1];
118 
119     cout<<((long long)1<<n)-a[n]<<endl;
120 }
121 void init()
122 {
123     string tmp;
124     ans.resize(103,tmp);
125     pre.resize(103,tmp);
126     pre[0]="1";
127     for(int i=1; i<103; i++)
128         pre[i]=sum(pre[i-1], pre[i-1]);
129 }
130 int main()
131 {
132     //freopen("input.txt","r",stdin);
133     init();
134     while(cin>>n>>k)
135     {
136 
137         if(n==k)
138         {
139             cout<<"1"<<endl;
140             continue;
141         }
142         if(n==k+1)
143         {
144             cout<<"3"<<endl;
145             continue;
146         }
147         if(k==1)
148         {
149             cout<<_minus(pre[n],"1")<<endl;
150             continue;
151         }
152         k--;
153         if(n<62)    //这里对抛62次以下的情况用longlong直接干掉。
154             cal63();
155         else    //大于62就用大数来算
156             cal64();
157 
158     }
159     return 0;
160 }
AC代码

 

转载于:https://www.cnblogs.com/xcw0754/p/4469515.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值