【10.5校内测试】【DP】【概率】

转移都很明显的一道DP题。按照不优化的思路,定义状态$dp[i][j][0/1]$表示吃到第$i$天,当前胃容量为$j$,前一天吃(1)或不吃(0)时能够得到的最大价值。

因为有一个两天不吃可以复原容量的定义,所以需要前一天的状态。

而注意,容量表示的是当前第$i$天吃之前的容量。

然后考虑压缩空间,将天数滚动。要注意的是滚动过后$now$指向的是$i$后一天的状态,因此刷表更新。

#include<bits/stdc++.h>
using namespace std;

int n, m;
int a[1005], dp[2][20005][3], ap[20005];

int main() {
    freopen("buffet.in", "r", stdin);
    freopen("buffet.out", "w", stdout);
    scanf("%d%d", &n, &m);
    int ans = 0;
    for(int i = 1; i <= n; i ++)    scanf("%d", &a[i]);
    ap[1] = m;
    for(int i = 2; i <= n; i ++) ap[i] = ap[i-1] * 2 / 3;
    int now = 0;
    memset(dp[now], -1, sizeof(dp[now]));
    dp[0][1][0] = 0;
    for(int i = 1; i <= n; i ++) {
        now ^= 1;
        memset(dp[now], -1, sizeof(dp[now]));
        for(int j = 1; j <= n; j ++) {
            if(~dp[now^1][j][0]) {
                dp[now][1][0] = max(dp[now][1][0], dp[now^1][j][0]);
                dp[now][j+1][1] = max(dp[now][j+1][1], dp[now^1][j][0] + min(a[i], ap[j]));
            }
            if(~dp[now^1][j][1]) {
                dp[now][j+1][1] = max(dp[now][j+1][1], dp[now^1][j][1] + min(a[i], ap[j]));
                dp[now][j-1][0] = max(dp[now][j-1][0], dp[now^1][j][1]);
            }
        }
    }
    for(int i = 1; i <= n; i ++)
        ans = max(ans, max(dp[now][i][1], dp[now][i][0]));
    printf("%d", ans);
    return 0;
}

概率神题,三校只有$yuli$dalao$A$掉了%%%%

非常神奇的记忆化搜索,就算我能把式子推出来也会弄晕的QAQ

定义$dp[i][j]$表示当前剩余$i$个人,当前编号为$j$的人的存活概率。枪在当前1号手中。

注意这个当前,表示的是此时剩下来的人重新从0编号。

可以推出转移式子:$dp[i][j]=q*dp[i-1][j-1]+(1-q)*dp[i][(j-kmodi+i)modi]$,$q$表示当前这枪打出去能打出来的概率。$q=(i-1)/C$,因为当前子弹比人数要少1。前一个式子是打出了这个枪,于是0死了,j在剩下的人中号数要-1,(1把枪移交2相当与2变为了现在的1),后面的式子是打了空枪,枪移交给后面第k个人,同样也是把整个队列往前移k位。

观察式子可以发现,$dp[i-1][j-1]$我们可以在记忆化搜索中递归求得,而后面$dp[i][(j-kmodi+i)modi]$是与$dp[i][j]$同层的,考虑怎么求得。

可以发现,如果一直打空枪,从$j$开始,一定可以通过环走回$j$。所以在递归边界式子变为$dp[i][j]=o+(1-q)^ndp[i][j]$,其中每个$(1-q)$虽然不同,但在搜索过程中可以顺便算出来。$o$表示的是在以后的层数中可以求得的,因为我们可以把$dp[i][(j-kmodi+i)modi]$带入最开始的转移式,再把它们按层数分离。然后就按分离出来的式子将每一步更新即可。

过程中记忆化即可。然后概率要用逆元,可以预处理出来。

#include<bits/stdc++.h>
#define LL long long
#define mod 1000000009
using namespace std;

int T, N, C, K;

int vf[1005];
int dp[1005][1005], vis[1005][1005];
LL mpow(int a, LL b) {
    LL ans = 1;
    for(; b; b >>= 1, a = 1ll * a * a % mod)
        if(b & 1)    ans = 1ll * ans * a % mod;
    return ans;
}

int dfs(int res, int pos, int oo, int gl) {
    if(pos == -1)    return 0;
    if(res == 1)    return 1;
    if(vis[res][pos] && dp[res][pos] == -1) {//同层中走回来了 可以直接算
        dp[res][pos] = 1ll * oo * mpow((1 - gl + mod) % mod, mod - 2) % mod;
        return dp[res][pos];
    }
    if(vis[res][pos])    return dp[res][pos];//记忆化
    vis[res][pos] = 1;
    dfs(res, (pos - K % res + res) % res, (oo + 1ll * gl * (res - 1) % mod * vf[C] % mod * dfs(res - 1, pos - 1, 0, 1) % mod) % mod, 1ll * gl * (C - res + 1) % mod * vf[C] % mod);//子弹共有res-1个
    if(~dp[res][pos])    return dp[res][pos];
    dp[res][pos] = (1ll * (res - 1) * vf[C] % mod * dfs(res - 1, pos - 1, 0, 1) % mod + 1ll * (C - res + 1) * vf[C] % mod * dp[res][(pos - K % res + res) % res] % mod) % mod;
    return dp[res][pos];
}

int main() {
    freopen("gun.in", "r", stdin);
    freopen("gun.out", "w", stdout);
    scanf("%d", &T);
    for(int i = 1; i <= 1000; i ++)    vf[i] = mpow(i, mod - 2);
    while(T --) {
        memset(vis, 0, sizeof(vis));
        memset(dp, -1, sizeof(dp));
        scanf("%d%d%d", &N, &C, &K);
        for(int i = 0; i < N; i ++)
            printf("%d ", dfs(N, i, 0, 1));
        printf("\n");
    }
}

原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9745140.html

时间: 2024-11-08 12:17:22

【10.5校内测试】【DP】【概率】的相关文章

【10.17校内测试】【二维数位DP】【博弈论/预处理】【玄学(?)DP】

Solution 几乎是秒想到的水题叻! 异或很容易想到每一位单独做贡献,所以我们需要统计的是区间内每一位上做的贡献,就是统计区间内每一位是1的数的数量. 所以就写数位dp辣!(昨天才做了数字统计不要太作弊啊!) Code #include<bits/stdc++.h> #define LL long long #define mod 1000000007 using namespace std; inline void read(LL &x) { x = 0; char ch = g

【10.22校内测试】【二分】【二分图】【很像莫队的乱搞/树状数组】

Solution 谁能想到这道题卡读入??还卡了70pts??? 二分+$n^2$check就行了 Code #include<bits/stdc++.h> using namespace std; int n, m; int sum[2005][2005]; void read(int &x) { x = 0; char ch = getchar(); while(ch > '9' || ch < '0') ch = getchar(); while(ch >= '

【10.27校内测试】【可删堆+拓排】

Solution 有向图要找最长路径的话,可以想到拓扑序转移.正反跑两边处理出每个点离起点和终点的最大值.访问每条边就能统计出经过每条边最长路径的长度. 问题是怎么统计出删除每个点的影响? 拓扑排序后,可以发现,删除层数靠后的点会对前面产生影响,因为此时想统计前面的边存在的最长路就不能判掉经过这个点的路径,所以只能按拓扑序从前往后删点. 这里直接说做法吧,维护一个大根堆,储存当前枚举到的最长路径,首先把每个点离终点的最大值推入堆中.每枚举删除一个点,就把它对前面点有影响的路径删掉,更新答案后再把

【10.7校内测试】【队列滑窗】【2-sat】【贪心+栈二分+线段树(noip模拟好题)】【生日祭!】

比较好想的一道题,直接用队列滑窗,因为扫一遍往队列里加东西时,改变的只有一个值,开桶储存好就行了! #include<bits/stdc++.h> using namespace std; int n, k, r; inline int min(int a, int b) { return a > b ? b : a; } inline int max(int a, int b) { return a > b ? a : b; } int sum[200005], q[200005

UVA 11427 Expect the Expected(DP+概率)

链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=35396 [思路] DP+概率 见白书. [代码] 1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 5 const int N = 100+10; 6 7 int n,a,b; 8 double f[N][N]; 9 10 int main() { 11 int T,kase=

【dp概率与期望】pattern

这是一个比赛题 营销策略 (pattern.cpp/c/pas) [题目描述] W 记的儿童套餐会赠送一份小玩具,赠送的小玩具共有n 种.小朋友买了m 份儿童套餐,求收集齐n 种小玩具的概率.假设每份儿童套餐赠送的小玩具的种类是等概率随机的. [输入格式] 从pattern.in 中输入数据一行,两个整数n,m. [输出格式] 输出到pattern.out 中一个实数表示收集齐小玩具的概率,保留4 位小数. [样例输入] 2 3 [样例输出] 0.7500 [数据规模与约定] 对于10% 的数据

CF235 Let&#39;s Play Osu![dp+概率]

题意: 给n个位置,给出1-n上每个位置出现O的概率pi,记分规则如下,连续的x个O记为x^2分,求和,如 XXOOOXOXOOXX得分为 求得分的期望 思考一下,我们能比较容易地得出O(n^2)的方法 令dp[i]为前i的得分期望 那么 显然这题 考虑一下变换记分的方式 我们有 那么记分方式就变为 一段连续的O,有多少对O×2+O的个数 一对O可以贡献2分 现在得分来源变为两个地方 一对O(2分),和单个O(1分) 我们知道 期望=概率×收益 我们找到每个对O的概率×2 再找到单个O×出现概率

开发者常用的10大GUI测试框架

1.Abbot - Java GUI 测试框架 Abbot是一个基于GUI的简单的Java测试框架,它能够帮助开发者测试Java用户界面. 它提供事件自动生成和验证Java GUI组件,使您能够轻松地启动,探索和控制应用程序.开发者可通过脚本和编译代码两种方式来使用Abbot框架,这就是为什么它被认为是在开发者的系统测试和QA的功能测试中都能用到的最完美的GUI测试工具. 2.EggPlant - GUI自动化测试工具 EggPlant是一个QA的GUI自动化测试工具,它是为使专业商业软件的应用

Jmeter测试中奖概率

最近做个项目需要测试中奖概率,又刚换的mac本win上的工具全不能用了,所以想到用jmeter实现. 实现步骤: 1.使用HTTP请求访问接口 2.添加查看结果器查看测试结果 3.添加聚合报告查看次数统计 2.使用JSON提取器提取json返回信息 3.添加调试取样器查看返回某个值的次数 原文地址:https://www.cnblogs.com/rslai/p/12544072.html