期望dp
期望的线性性质:E(ax+by)=aE(X)+bE(Y)=p(X)×E(X)+p(Y)×E(Y)
dp(状态)的值:在当前状态下还需要多少论才能结束答案 所以输出的是dp(初始状态) dp(目的状态=0)
dp[i][j]=p1 * ( dp[i-1][j-1] + 1 ) + p2 * (dp[i-1][j-2] + 1 ) //+1是权值
1-n总长度的期望
到达某个结果的期望值 = 这个结果 * 从起始状态到这个状态的概率
f[i]=∑1/k*(w[i]+f(S[i])
f[i]表示从i走到n的期望 边界f(N)=0即n到n的期望是0 答案为f[1]
没有权值 : p(x)=p1x1+p2x2+....+pnxn
有权值: p(y)=(p1(x1+w)+p2(x2+w)+…+pn(xn+w))/n
从x走到y: E(y)=E(x)+∑pi∗wi
#include <iostream>
#include <cstring>
using namespace std;
const int N = 100010 , M = 2 * N;
double f[N];
int e[M] , ne[M] , w[M] , h[N] , idx;
int n ,m;
int d[N];
void add(int a , int b , int c)
{
e[idx] = b , ne[idx] = h[a] ; w[idx] = c , h[a] = idx++;
}
double dp(int u)//记忆化搜索
{
if(f[u] >= 0) return f[u];//如果见过
f[u] = 0;
for(int i = h[u] ; ~i ; i = ne[i])
{
int j = e[i];
f[u] += (w[i] + dp(j)) / d[u];//d表示出边多少 dp(j)这里使用了倒推
}
return f[u];
}
int main()
{
cin >> n >> m;
memset(h , -1 , sizeof h);
while(m--)
{
int a , b , c;
cin >> a >> b >> c;
add(a , b , c);
d[a]++;//出度++
}
memset(f , -1 , sizeof f);//初始化成一个不存在的数
printf("%.2lf\n" , dp(1));//从1开始
return 0;
}
扑克牌https://www.acwing.com/solution/content/49466/ 在一副牌里面臭拿a,b,c,d各个花色张牌 去拿到的期望
我们使用f(a, b, c, d, x, y)表示现在已经翻出a张黑桃,b张红桃,c张梅花,d张方块,且大小王的状态为x、y需要翻开的牌的张数的期望值。
大王或者小王,Admin 将会把它作为某种花色的牌放入对应堆中 所以其中x、y取值为04,03分别表示黑桃、红桃、梅花、方块,4表示还没有被翻出。
当前翻开的牌数记为sum,则sum = a+b+c+d+(x!=4)+(y!=4),考虑状态转移:
边界是如果所有花色的牌都达到的数量,则不用再翻牌了,返回0即可。最终的答案就是f(0, 0, 0, 0, 4, 4)。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 14;
const double INF = 1e20;
int A, B, C, D;
double f[N][N][N][N][5][5];
double dp(int a, int b, int c, int d, int x, int y) {
double &v = f[a][b][c][d][x][y];
if (v >= 0) return v;
int as = a + (x == 0) + (y == 0);
int bs = b + (x == 1) + (y == 1);
int cs = c + (x == 2) + (y == 2);
int ds = d + (x == 3) + (y == 3);
if (as >= A && bs >= B && cs >= C && ds >= D) return v = 0;
int sum = a + b + c + d + (x != 4) + (y != 4);
sum = 54 - sum;
if (sum <= 0) return v = INF; // 没有牌可以翻开了
v = 1; // 表示翻开一张牌
if (a < 13) v += (13.0 - a) / sum * dp(a + 1, b, c, d, x, y);
if (b < 13) v += (13.0 - b) / sum * dp(a, b + 1, c, d, x, y);
if (c < 13) v += (13.0 - c) / sum * dp(a, b, c + 1, d, x, y);
if (d < 13) v += (13.0 - d) / sum * dp(a, b, c, d + 1, x, y);
if (x == 4) {
double t = INF;
for (int i = 0; i <= 3; i++) t = min(t, 1.0 / sum * dp(a, b, c, d, i, y));
v += t;
}
if (y == 4) {
double t = INF;
for (int i = 0; i <= 3; i++) t = min(t, 1.0 / sum * dp(a, b, c, d, x, i));
v += t;
}
return v;
}
int main() {
cin >> A >> B >> C >> D;
memset(f, -1, sizeof f);
double t = dp(0, 0, 0, 0, 4, 4);
if (t > INF / 2) t = -1;
printf("%.3lf", t);
return 0;
}
作者:ToLoveToFeel
链接:https://www.acwing.com/solution/content/49466/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
牛客麻将题 给定初始手牌 求最优策略下达到七对的期望轮数
最优:不会发生扔什么来什么 扔了之后不会捡回来
第一 关心手上还有多少个单着 卡池剩下多少张单的
第二 扔出的肯定是该扔的
dp[i][j]表示卡池中还剩i张没有摸过的牌 手上还有j张单牌 还需要多少次摸排才能胡牌的期望
dp[i][j]=p1 * ( dp[i-1][j-1] + 1 ) + p2 * (dp[i-1][j-2] + 1 )
p1概率是摸到单牌的概率,所以池子少了一张单牌但是手上的牌不变 +1表示新增了一轮摸牌了
p2表示摸到的牌可以和已经有的牌凑成了一对,所以池子里面少了一张手上的单牌少了一张 +1表示新增了一轮摸牌了
+1的值可能随题目要求改变
p1= (i-3j)/i //卡牌里面的牌数 - 3*j单牌 抽到不能凑对的牌
p2= (3j)/i
#include<bits/stdc++.h>
using namespace std;
int t;
map<string, int> cnt;
long long dp[150][15];
const long long mod = 1e9+7;
long long ksm(long long x,long long y)
{
long long ans=1, tmp = x;;
while(y)
{
if(y&1) ans = ans * tmp % mod;
y >>= 1;
tmp = tmp * tmp % mod;
}
return ans;
}
long long inv(long long x)
{
return ksm(x, mod-2);
}
int dfs(int i, int j)//i为牌堆里面的牌 j为手上单牌
{
if (dp[i][j] != -1) return dp[i][j];
if (j == 0) return 0;
long long p1 = (i-3*j) *inv(i) % mod;
// if (i-3*j <= 0 ) p1 = 0;
long long p2 = (3*j) *inv(i) % mod;
//p1抽到其他单牌 p2抽到对应单牌
if(j==1) dp[i][j] = (p1* (dfs(i-1, j)+1) % mod +p2 * 1 % mod) %mod; //手里面只剩一张牌的时候可以直接胡牌 不需要花费轮数 +1
else dp[i][j] = (p1* (dfs(i-1, j)+1) % mod +p2 * (dfs(i-1, j-2)+1)% mod) %mod;
return dp[i][j];
}
int main()
{
scanf("%d", &t);
memset(dp, -1, sizeof(dp));
int cas =0;
while (t--)
{
string s;
cin >> s;
cnt.clear();
for (int i = 0; i < 13; i++)
{
string c = "";
c += s[2*i];
c += s[2*i+1];
cnt[c]++;
}
int n = 0;
for(auto it : cnt)
{
if (it.second == 1) n++;
}
cout <<"Case #"<<++cas<<": "<< dfs(123, n) << endl;
}
}