2021——概率dp

1.POJ3744 Scout YYF I

  输入n表示共有n个地雷(0<n<=10),并且输入每个地雷所在的位置ai(ai为不大于10^8的正整数)。

  现在求从1号位置出发越过所有地雷的概率。用两种行走方式:①走一步  ②走两步(不会踩爆中间那个雷)。这两个行为的概率分别为p和(1-p)。

 

我们首先考虑每个点要跨过地雷,一定是在地雷前一步的位置走了一个两步的方案,所以我们将每个块的概率相乘,这样问题就是成为了计算到每个地雷前一个点的概率

这个就可以用到概率dp进行计算

f[i]=f[i-1]*p+f[i-2]*(1-p)

然后加上一个矩阵快速幂加速就行了

 

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int maxn=1005;
int n,a[maxn];
double p;
struct Matrix
{
	double s[5][5];
	void init(double p)
	{
		s[1][1]=p; s[1][2]=1-p;
		s[2][1]=1; s[2][2]=0;
	}
	Matrix operator *(const Matrix &oth) const
	{
		Matrix c={};
		for(int k=1;k<=2;k++)
			for(int i=1;i<=2;i++)
				for(int j=1;j<=2;j++)
					c.s[i][j]+=s[i][k]*oth.s[k][j];
		return c;
	}
};
Matrix qpow(Matrix aa,int b)
{
	Matrix base={};
	for(int i=1;i<=2;i++) base.s[i][i]=1;
	while(b>0)
	{
		if(b&1) base=base*aa;
		aa=aa*aa; b>>=1;
	}
	return base;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	while(~scanf("%d%lf",&n,&p))
	{
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		sort(a+1,a+n+1);
		double ans=1;
		for(int i=1;i<=n;i++)
		{
			if(a[i]==a[i-1]+1) 
			{
				ans=0;
				break;
			}
			Matrix cur; 
			cur.init(p);
			cur=qpow(cur,a[i]-a[i-1]-2);
			ans*=cur.s[1][1]*(1-p);
		}
		printf("%.7lf\n",ans);
	}
	return 0;
}

 

2.P2059 [JLOI2013]卡牌游戏

 

这道题目是很好的概率dp

 

代码

#include<bits/stdc++.h>
using namespace std;
int n,m;
const int maxn=55;
int a[maxn];
double f[maxn][maxn];
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) scanf("%d",&a[i]);
	f[1][1]=1.0;
	for(int i=2;i<=n;i++)
		for(int j=1;j<=i;j++)
			for(int k=1;k<=m;k++)
			{
				int c=(a[k]%i==0)?i:a[k]%i;
				if(c>j) f[i][j]+=f[i-1][i-c+j]/(double)m;
				else if(c<j) f[i][j]+=f[i-1][j-c]/(double)m;
			}
	for(int i=1;i<=n;i++)
		printf("%.2lf%% ",f[n][i]*100.0);
	return 0;
}

 

3.P1654 OSU!

 

代码

#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn=1e5+5;
double p[maxn],f1[maxn],f2[maxn],ans[maxn];
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lf",&p[i]);
	for(int i=1;i<=n;i++)
	{
		f1[i]=(f1[i-1]+1)*p[i];
		f2[i]=(f2[i-1]+2*f1[i-1]+1)*p[i];
		ans[i]=ans[i-1]+(3*f1[i-1]+3*f2[i-1]+1)*p[i];
	}
	printf("%.1f",ans[n]);
	return 0;
}

 

4.P1850 [NOIP2016 提高组] 换教室

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,e,v;
const int maxn=2005;
const double inf=1e18;
double p[maxn],dp[maxn][maxn][2];
int c[maxn],d[maxn],h[maxn];
int mp[maxn][maxn];
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&v,&e);
	for(int i=1;i<=n;i++) scanf("%d",&c[i]);
	for(int i=1;i<=n;i++) scanf("%d",&d[i]);
	for(int i=1;i<=n;i++) scanf("%lf",&p[i]);
	int x,y,z;
	memset(mp,63,sizeof(mp));
	for(int i=1;i<=e;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		mp[x][y]=mp[y][x]=min(z,mp[x][y]);
	}
	for(int k=1;k<=v;k++)
		for(int i=1;i<=v;i++)
			for(int j=1;j<=v;j++)
				mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);
	for(int i=1;i<=v;i++) mp[i][i]=0,mp[0][i]=0,mp[i][0]=0;
	for(int i=0;i<=n;i++)
		for(int j=0;j<=m;j++)
			dp[i][j][0]=dp[i][j][1]=inf;
	dp[1][0][0]=dp[1][1][1]=0;
	for(int i=2;i<=n;i++)
	{ 
		dp[i][0][0]=dp[i-1][0][0]+mp[c[i-1]][c[i]];
		for(int j=1;j<=min(i,m);j++)
		{
			dp[i][j][0]=min(dp[i][j][0],dp[i-1][j][0]+mp[c[i-1]][c[i]]);
			dp[i][j][0]=min(dp[i][j][0],dp[i-1][j][1]+mp[c[i-1]][c[i]]*(1-p[i-1])+mp[d[i-1]][c[i]]*p[i-1]);
			dp[i][j][1]=min(dp[i][j][1],dp[i-1][j-1][0]+mp[c[i-1]][d[i]]*p[i]+mp[c[i-1]][c[i]]*(1-p[i]));
			dp[i][j][1]=min(dp[i][j][1],dp[i-1][j-1][1]+mp[c[i-1]][c[i]]*(1-p[i-1])*(1-p[i])+mp[c[i-1]][d[i]]*p[i]*(1-p[i-1])+mp[d[i-1]][c[i]]*p[i-1]*(1-p[i])+mp[d[i-1]][d[i]]*p[i-1]*p[i]);
		}
	}
	double ans=inf;
	for(int i=0;i<=m;i++) 
		ans=min(ans,min(dp[n][i][0],dp[n][i][1]));	
	printf("%.2lf",ans); 
	return 0;
}

 

5.P4035 [JSOI2008]球形空间产生器

高斯消元 / 以前也用模拟退火做过一次

代码

#include<bits/stdc++.h>
using namespace std;
int n;
double a[12][12],b[12],c[12][12];
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n+1;i++)
		for(int j=1;j<=n;j++)
			scanf("%lf",&a[i][j]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			c[i][j]=2*(a[i][j]-a[i+1][j]);
			b[i]+=a[i][j]*a[i][j]-a[1+i][j]*a[i+1][j];
		}
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<=n;j++)
		{
			if(c[j][i]>1e-8)
			{
				for(int k=1;k<=n;k++)
					swap(c[i][k],c[j][k]);
				swap(b[i],b[j]);
			}
		}
		for(int j=1;j<=n;j++)
		{
			if(i==j) continue;
			double am=c[j][i]/c[i][i];
			for(int k=1;k<=n;k++) c[j][k]-=c[i][k]*am;
			b[j]-=b[i]*am;
		}
	}
	for(int i=1;i<=n;i++) printf("%.3lf ",b[i]/c[i][i]);
	return 0;
} 

 

6.BZOJ3270 博物馆

题目描述

 

有一天Petya和他的朋友Vasya在进行他们众多旅行中的一次旅行,他们决定去参观一座城堡博物馆。这座博物馆有着特别的样式。它包含由m条走廊连接的n间房间,并且满足可以从任何一间房间到任何一间别的房间。

两个人在博物馆里逛了一会儿后两人决定分头行动,去看各自感兴趣的艺术品。他们约定在下午六点到一间房间会合。然而他们忘记了一件重要的事:他们并没有选好在哪儿碰面。等时间到六点,他们开始在博物馆里到处乱跑来找到对方(他们没法给对方打电话因为电话漫游费是很贵的)

不过,尽管他们到处乱跑,但他们还没有看完足够的艺术品,因此他们每个人采取如下的行动方法:每一分钟做决定往哪里走,有Pi 的概率在这分钟内不去其他地方(即呆在房间不动),有1-Pi 的概率他会在相邻的房间中等可能的选择一间并沿着走廊过去。这里的i指的是当期所在房间的序号。在古代建造是一件花费非常大的事,因此每条走廊会连接两个不同的房间,并且任意两个房间至多被一条走廊连接。

两个男孩同时行动。由于走廊很暗,两人不可能在走廊碰面,不过他们可以从走廊的两个方向通行。(此外,两个男孩可以同时地穿过同一条走廊却不会相遇)两个男孩按照上述方法行动直到他们碰面为止。更进一步地说,当两个人在某个时刻选择前往同一间房间,那么他们就会在那个房间相遇。

两个男孩现在分别处在a,b两个房间,求两人在每间房间相遇的概率。

 


输入格式

 

第一行包含四个整数,n表示房间的个数;m表示走廊的数目;a,b (1 ≤ a, b ≤ n),表示两个男孩的初始位置。

之后m行每行包含两个整数,表示走廊所连接的两个房间。

之后n行每行一个至多精确到小数点后四位的实数 表示待在每间房间的概率。

题目保证每个房间都可以由其他任何房间通过走廊走到。

 


输出格式

 

输出一行包含n个由空格分隔的数字,注意最后一个数字后也有空格,第i个数字代表两个人在第i间房间碰面的概率(输出保留6位小数)

注意最后一个数字后面也有一个空格

 


样例输入

2 1 1 2
1 2
0.5
0.5

样例输出

0.500000 0.500000

提示

 

对于100%的数据有 n <= 20,n-1 <= m <= n(n-1)/2

 


题目来源

高斯消元

 

 

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=22;
const int maxm=405;
int n,m,x,y,cnt,d[maxn],id[maxn][maxn];
vector <int> G[maxn];
double p[maxn],a[maxm][maxm],ans[maxm];
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&x,&y);
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
		d[u]++; d[v]++;
	}
	for(int i=1;i<=n;i++)
		scanf("%lf",&p[i]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			id[i][j]=++cnt;
			if(i!=j) a[cnt][cnt]=p[i]*p[j];
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(i!=j)
			{
				for(int k=0;k<G[i].size();k++)
				{
					int t=G[i][k];
					a[id[t][j]][id[i][j]]+=(1-p[i])*p[j]/d[i];
				}
				for(int k=0;k<G[j].size();k++)
				{
					int t=G[j][k];
					a[id[i][t]][id[i][j]]+=p[i]*(1-p[j])/d[j];
				}
				for(int k1=0;k1<G[i].size();k1++)
					for(int k2=0;k2<G[j].size();k2++)
					{
						int t1=G[i][k1],t2=G[j][k2];
						a[id[t1][t2]][id[i][j]]+=(1-p[i])*(1-p[j])/(d[i]*d[j]);
					}
			}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			a[id[i][j]][id[i][j]]-=1.0;
	a[id[x][y]][cnt+1]=-1;
	for(int i=1;i<=cnt;i++)
	{
		int tmp=i;
		for(int j=i+1;j<=cnt;j++)
			if(abs(a[j][i])>abs(a[tmp][i]))
				tmp=j;
		if(tmp!=i)
		{
			for(int j=1;j<=cnt+1;j++)
				swap(a[tmp][j],a[i][j]);
			for(int j=i+1;j<=cnt;j++)
			{
				double am=a[j][i]/a[i][i];
				for(int k=i;k<=cnt+1;k++)
					a[j][k]-=a[i][k]*am;
			}
		}
	}
	ans[cnt]=a[cnt][cnt+1]/a[cnt][cnt];
	for(int i=cnt-1;i>=1;i--)
	{
		double am=a[i][cnt+1];
		for(int j=i+1;j<=cnt;j++)
			am-=a[i][j]*ans[j];
		ans[i]=am/a[i][i];
	}
	for(int i=1;i<=n;i++)
		printf("%.6lf ",ans[id[i][i]]);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值