学了概率dp,可能练了二分
1.CodeForces - 148D Bag of mice
题目:公主(先手)和龙抓老鼠 ,有黑鼠b只,白鼠w只,只有公主抓到白鼠才算公主赢。龙每抓走一只老鼠,有另一只老鼠跳出袋子,求公主赢的概率。
设dp[i][j](i是剩下i只白鼠,j是剩下黑鼠)
最后结束的情况:
1.公主拿到了白鼠:dp[i][j]=i/(i+j);
2.龙拿到白鼠,公主赢概率为0;
过程情况:
1.公主(黑)和龙(黑)拿完后,跳出黑鼠:dp[i][j]+=dp[i][j-3]* j/(i+j) * (j-1)/(i+j-1) * (j-2)/(i+j-2);
2.公主(黑)和龙(黑)拿完后,跳出白鼠:dp[i][j]+=dp[i][j-2]* j/(i+j) * (j-1)/(i+j-1) * i/(i+j-2);
dp[i][j]表示的是在经历这些过程后,公主赢的概率。
初始化:要分别初始化0只白鼠和0只黑鼠的情况;
如果不初始化直接循环的话/(i+j-2)的部分会报错。另外注意输出的是f【w】【b】,不要反了。
代码:
#include<bits/stdc++.h>
using namespace std;
double f[1003][1003];//bai hei
//bai win
int main()
{
int w,b;cin>>w>>b;
for(int i=0;i<=w;i++)f[i][0]=1.0;
for(int i=0;i<=b;i++)f[0][i]=0.0;
for(int i=1;i<=w;i++){
for(int j=1;j<=b;j++){
f[i][j]=1.0*i/(i+j);//win
if(j>=3)//three black
f[i][j]+=1.0*j/(i+j)*1.0*(j-1)/(i+j-1)*(j-2)/(i+j-2)*1.0*f[i][j-3];
if(j>=2)//two black one white
f[i][j]+=1.0*j/(i+j)*1.0*(j-1)/(i+j-1)*i/(i+j-2)*1.0*f[i-1][j-2];
}
}
cout<<fixed<<setprecision(9)<<f[w][b];
return 0;
}
2.poj2096 Collecting Bugs
2096 -- Collecting Bugs (poj.org)
参考题解:【POJ 2096】Collecting Bugs 概率期望dp
题目:一共要n个A和s个B,一天你能找到一个东西(属于A属于B),它可能属于你找到过的A也可能属于你没找过的A,B同。问找到所有n个A和s个B的期望天数。
思路:计算期望E=∑所有可能需要的天数*概率
简单画个示意图来说,如下图所示:
问A到D的期望时间,所以E(A)=1+p1*E(B)+p2*E(C);
设f[i][j]是已经找到了i个A和j个B所花的天数;
f[i][j]=p1*f[i][j]+p2*f[i][j+1]+p2*f[i][j+1]+p3*f[i+1][j]+p4*f[i+1][j+1]+1;
其中,p1=(i*j)/(n*s),p2=(i/n)*(1-j/s),p3=(1-i/n)*(j/s),p4=(1-i/n)*(1-j/s);
所以移一下项,通一下分,得出:
f[i][j]=(1.0*(n-i)*j*f[i+1][j]+1.0*(s-j)*i*f[i][j+1]+1.0*(s-j)*(n-i)*f[i+1][j+1]+s*n)/(1.0*s*n-i*j);
代码:
#include<iostream>
#include <iomanip>
using namespace std;
double f[1003][1003];
int main()
{
int n,s;cin>>n>>s;
for(int i=n;i>=0;i--)
for(int j=s;j>=0;j--){
if(i==n&&j==s)continue;
f[i][j]=(1.0*(n-i)*j*f[i+1][j]+
1.0*(s-j)*i*f[i][j+1]+
1.0*(s-j)*(n-i)*f[i+1][j+1]+s*n)
/(1.0*s*n-i*j);
}
cout<<fixed<<setprecision(9)<<f[0][0];
return 0;
}
3.Educational Codeforces Round 118 C. Poisoned Dagger
题目:勇士要带毒药杀巨龙,毒药持续时间K秒;输入攻击时间,每次攻击会更新毒药持续时间;在能杀死血条为H的巨龙的条件下,问K的最小值。
用二分法+模拟。
二分的模板为:(下面是左闭右开的情况)
二分查找:
mid=l+(r-l)/2;
[0,n)--------while left<right;left = mid+1;right=mid;
[0,n-1]--------while left<=right;left = mid+1;right=mid-1;
二分问题:
mid=l+(r-l+1)/2;if(test(mid)) l=mid;else r=mid-1;
mid=(l+r)/2;if(test(mid)) r=mid;else l=mid+1;
代码:
#include<bits/stdc++.h>
using namespace std;
long long a[101];long long n,h;
bool check(long long mid){
long long ti=h;
for(int i=1;i<n;i++){
if(a[i+1]-a[i]<=mid) ti=ti-(a[i+1]-a[i]);
else ti=ti-mid;
if(ti<=0)return 1;
}
ti=ti-mid;
if(ti<=0)return 1;
return 0;
}
int main()
{
int T;cin>>T;
while(T--){
cin>>n>>h;
for(int i=1;i<=n;i++) cin>>a[i];
long long l=0,r=1e18+1,mid;
while(r>l){
mid=(r+l)/2;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<r<<endl;
}
return 0;
}
4.Codeforces1623C Balanced Stone Heaps
题目: 第i堆石头取出3d个石头,往前两堆石头i-1放d个石头,i-2放2d个石头,问最少的石头的堆最多有多少石头。
思路就是模拟,每个堆卡在mid这个线上,同时前两个堆要在mid的线上,这样子check返回真。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,h[200005],b[200005];
bool check(int mid){
for(int i=1;i<=n;i++)b[i]=h[i];
for(int i=n;i>=3;i--){
if(b[i]<mid)return 0;
if(b[i]>=mid){
int d=min((b[i]-mid)/3,h[i]/3);
b[i-1]+=d;b[i-2]+=2*d;
}
}
return b[1]>=mid&&b[2]>=mid;
}
signed main()
{
int T;cin>>T;
while(T--){
cin>>n;int maxx=0;
for(int i=1;i<=n;i++){cin>>h[i];maxx=max(maxx,h[i]);}
int l=0,r=maxx+1,mid,ans;
while(l<r){
mid=l+(r-l+1)/2;
if(check(mid)){l=mid;}
else r=mid-1;
}
cout<<l<<endl;
}
return 0;
}
5. 牛客练习赛98 AIR Triplet
C-AIR Triplet_牛客练习赛98 (nowcoder.com)
ai⊕aj=0,aj⊕ak=0:这意味着ai=aj=ak!!!
6. 牛客练习赛50tokitsukaze and Connection
tokitsukaze and Connection (nowcoder.com)
我用vector记录26个字母的下标判断是否连续,但其他人有更简单的做法。
#include<bits/stdc++.h>
using namespace std;
int vis[100];
int main()
{
int n;
string s;
cin>>n>>s;
for(int i=1;i<n;i++){
if(s[i-1]==s[i])continue;
else{
vis[s[i-1]]=1;
if(vis[s[i]]==1){printf("NO\n");return 0;}
}
}
printf("YES\n");
}