期望DP 训练专题

【spoj Favorite Dice && 洛谷p4550】


spoj的是简单版
洛谷的是稍复杂版
区别是复杂版在简单版的基础上多做了一次期望dp
也可以用期望的平方理解 但还不会…
链接分别如下:
spoj简单版.
洛谷复杂版.

下面先贴代码 有时间再来补解析:

#include<bits/stdc++.h>
#define maxn 10005
using namespace std;
double dp[maxn],ans[maxn];  
/*----------洛谷p4550 && spoj Favorite Dice-------------*\ 

 dp[n-i]  表示n-i个出现过, 直到取完还需的期望次数  
 ans[n-i] 表示n-i个出现过,直到取完还需的期望花费 
 转移方程 如下 
 dp[n-i]=(n-i)/n*dp[n-i]+i/n*dp[n-i+1]+1 
 ans[n-i]=(n-i)/n*ans[n-i]+i/n*ans[n-i+1]+dp[n-i]
 移项后即是代码中的转移方程  

\*------------------------------------------------------*/

int main()
{
	int n;
	scanf("%d",&n);
	dp[n]=0,ans[n]=0;
	for(int i=1;i<=n;i++){
		dp[n-i]=dp[n-i+1]+1.0*n/i;
		ans[n-i]=ans[n-i+1]+dp[n-i]*n/i;
	}
	printf("%.2lf\n",ans[0]);
	return 0;
} 

【Robots 2019 ACM-ICPC 南京赛区网络赛 D】


题意:对于一个DAG(有向无环图),每天在一个节点上有等可能的机会选择走到下一个节点或者不动,花费的代价时当前的天数,发现与上题类似,也是一个天数一个花费,且花费为天数,因此采用类似的dp两次的思路。

代码如下:(感谢学长帮忙写的拓扑)

#include<bits/stdc++.h>
#define ll long long
#define mem(b,t) memset(b,t,sizeof(b))
#define INF 0x3f3f3f3f
#define rep(i,n,m) for(int i=n;i<=m;i++)
#define per(i,n,m) for(int i=n;i>=m;i--)
#define bug1(i) cout<<i<<endl
#define bug2(i,j) cout<<i<<" "<<j<<endl
#define maxn 100005
#define mod 1000000007
using namespace std;

struct TPSORT{
	int n;
	vector<int> G[maxn];
	int d[maxn];
	double dp1[maxn],dp2[maxn];
	//dp1[i] 表示从i到终点期望的天数
	//dp2[i] 表示从i到终点期望的花费
	int dd[maxn];

	void init(int _n){
		n=_n;
		for(int i=1;i<=n;++i) G[i].clear(), dp1[i]=dp2[i]=0;
	}

	void addedge(int u, int v){
		G[u].push_back(v);
	}
	//拓扑排序1
	void work1(){
		queue<int> q;
		dp1[n]=-1;

		for(int i=1;i<=n;++i) // cal du
			for(int v:G[i]) d[v]++;

		for(int i=1;i<=n;++i) dd[i]=d[i];

		for(int i=1;i<=n;++i) if(!d[i]) q.push(i); // push start

		while(q.size()){
			int u=q.front(); q.pop();
			dp1[u]++;
			if(dd[u])
			dp1[u]=dp1[u]*(dd[u]+1.0)/dd[u];
			for(int v:G[u]){
				dp1[v]+=1.0*dp1[u]/(dd[v]+1);
				if(!(--d[v])) q.push(v);
			}
			
		}
	}
	//拓扑排序2
	void work2(){
		queue<int> q;
		dp2[n]=-dp1[n];

		for(int i=1;i<=n;++i) // cal du
			for(int v:G[i]) d[v]++;

		for(int i=1;i<=n;++i) dd[i]=d[i];

		for(int i=1;i<=n;++i) if(!d[i]) q.push(i); // push start

		while(q.size()){
			int u=q.front(); q.pop();
			dp2[u]+=dp1[u];
			if(dd[u])
			dp2[u]=dp2[u]*(dd[u]+1.0)/dd[u];
			for(int v:G[u]){
				dp2[v]+=1.0*dp2[u]/(dd[v]+1);
				if(!(--d[v])) q.push(v);
			}
		}
	}
	
	
}TpSort;

int main(){
	int t;
	cin>>t;
	while(t--){
		int n,m;
		cin>>n>>m;
		TpSort.init(n);
		while(m--){
			int x,y;
			cin>>x>>y;
			TpSort.addedge(y,x);
		}
		TpSort.work1();
		TpSort.work2();
		printf("%.2lf\n",TpSort.dp2[1]);
	}
	return 0;
}

【Table Tennis 2020 ICPC Asia Taiwan】

#include<bits/stdc++.h>
#define ll long long
#define mem(b,t) memset(b,t,sizeof(b))
#define INF 0x3f3f3f3f
#define rep(i,n,m) for(int i=n;i<=m;i++)
#define per(i,n,m) for(int i=n;i>=m;i--)
#define bug1(i) cout<<i<<endl
#define bug2(i,j) cout<<i<<" "<<j<<endl
#define maxn 1005
#define db double
#define eps 1e-6
#define pi acos(-1)
using namespace std;
double dp[maxn][maxn];
double ans1,ans2,ans;
void cal(int n){
	double t;
	rep(i,1,n-2){
		rep(j,i+1,n-1){
			rep(k,j+1,n){
				if(n%2==1) t=ans2;
				else t=ans1;
				if(i%2==1) t*=ans1;
				else t*=ans2;
				if(j%2==1) t*=ans1;
				else t*=ans2;
				if(k%2==1) t*=ans1;
				else t*=ans2;
				rep(p,1,n){
					if(p!=i&&p!=j&&p!=k){
						if(p%2==1) t*=(1-ans1);
						else t*=(1-ans2);
					}
				}
				ans+=t;
			}
		}
	}
	
}
int main(){
	int n;
	scanf("%d",&n);
	while(n--){	
		rep(i,0,1000)
			rep(j,0,1000)
				dp[i][j]=0;
		double p1,p2;
		ans=0,ans1=0,ans2=0;
		scanf("%lf %lf",&p1,&p2);
		dp[0][0]=1;
		rep(i,0,1000){
			rep(j,0,1000){
				if(!dp[i][j]) continue;
				int tot=i+j;
				if(max(i,j)>=11 && abs(j-i)>1){
					if(i>j) ans1+=dp[i][j];
					continue;
				}
				tot++;
				double p;
				if(tot<=20){
					if((tot-1)%4/2==1) p=1-p2;
					else p=p1;	
				}
				else {
					if(tot%2==1) p=p1;
					else p=1-p2;
				}
				dp[i+1][j]+=dp[i][j]*p;
				dp[i][j+1]+=dp[i][j]*(1-p);
			}
		}
//		cout<<ans1<<endl;

		rep(i,0,1000)
			rep(j,0,1000)
				dp[i][j]=0;
		dp[0][0]=1;
		rep(i,0,1000){
			rep(j,0,1000){
				if(!dp[i][j]) continue;
				int tot=i+j;
				double t=dp[i][j];
				if(max(i,j)>=11 && abs(j-i)>1){
					if(i>j) ans2+=dp[i][j];
					continue;
				}
				tot++;
				double p;
				if(tot<=20){
					if((tot-1)%4/2==1) p=p1;
					else p=1-p2;	
				}
				else {
					if(tot%2==1) p=1-p2;
					else p=p1;
				}
				dp[i+1][j]+=dp[i][j]*p;
				dp[i][j+1]+=dp[i][j]*(1-p);
			}
		}
//		cout<<ans2<<endl;
		rep(i,3,6){
			cal(i);
		}
		printf("%.10lf\n",ans);
	}
    return 0;
}
/*
4
0.25 0.75
0.5 0.5 
0.4 0.6 
0.75 0.25
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值