【总结】三道神一样のdp

来源:互联网 发布:水利工程力学分析软件 编辑:程序博客网 时间:2024/06/08 13:21

拿到题我觉得是两道数论加一道网络流。结果是三道dp!!

orz dp真的好渣啊 感觉快要没救了 

呜呜呜尧神对不起啊真的是考得不好心情很差一大堆负能量_(:з)∠)_


1、稳住GCD

【题目描述】

给你一组数,a1,a2,a3,...,an。
令:G=gcd(a1,a2,a3,...,an)
现在从中任意删除一些数字,设剩下的数为:al1,al2,al3,...,alm。
再令:g=gcd(al1,al2,al3,...,alm)
现要求G=g,问最多能删除多少数?

【输入】

第一行一个数n,第二行n个数a1,a2,a3,...,an。

【输出】

输出只有一个数,表示最多能删除多少数。

【样例输入】

3
4 6 8

【样例输出】

1

【数据范围】

20%的数据,1≤n≤10;
50%的数据,1≤n≤100;
100%的数据,1≤n≤700,1≤ai≤10000;


题解

总感觉想出这个dp的人脑洞很大啊……(不 其实是我题做太少orz)

dp[i][j]表示确定到第i个数 gcd为j时 最少保留数的数目
转移:dp[i][j] = min(dp[i][k] + 1)   其中gcd(a[i], k) == j
然后暴力转移就行了。。。第一维可以去掉变成一个滚动数组(要倒叙枚举j)

考试的时候打了个素数表和一个二进制枚举 骗了50分2333


#include <cstdio>#include <iostream>#include <cstring>using namespace std;int read(){int sign = 1, n = 0; char c = getchar();while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }return sign*n;}int N, a[705];int dp[10005];int gcd(int a, int b){for(int t = a % b; t; a = b, b = t, t = a % b);return b;}int main(){freopen("gcd.in", "r", stdin);freopen("gcd.out", "w", stdout);N = read();for(int i = 1; i <= N; ++i) a[i] = read();int G = 0; memset(dp, 0x3f, sizeof(dp)); dp[0] = 0;for(int i = 1; i <= N; ++i){G = gcd(G, a[i]);for(int j = 10000; j >= 0; --j){int Gcd = gcd(j, a[i]);dp[Gcd] = min(dp[Gcd], dp[j] + 1);}}printf("%d\n", N - dp[G]);return 0;}

2、骰子游戏

【题目描述】

Analysis和他的伙伴一共N个小朋友一起玩游戏。一开始N个小朋友排成一排,其中An
alysis排在第M位。然后他们按照以下的策略进行游戏:
1. 如果只剩下一个小朋友了,那么他就是赢家;
2. 否则掷一个骰子:
(1) 如果掷到4,那么排在第一位的小朋友胜出;
(2) 如果掷到1,3,5,那么排在第一位的小朋友从队首走到队尾;
(3) 如果掷到2,6,那么排在第一位的小朋友出局,直接离开游戏。
假设骰子均匀,Analysis想知道自己的胜率是多少。

【输入】

一行两个整数N、M,意义如题目描述。

【输出】

一个实数,表示Analysis的胜率。保留9位小数。

【样例输入】

3 2

【样例输出】

0.317460317

【数据范围】

100%的数据,M ≤ N ≤ 1 000。

题解

这道题……挺水的……就是推公式有点麻烦QAQ 然后考试的时候………………没写完…………
本来是打算打个表的 结果手抖了一个地方orz。。。

dp[i][j] 表示剩余i个人 第j个人的胜率
显然 dp[i][1] = 1/6 + 1/2 * dp[i][i]
dp[i][j] = 1/2 * dp[i][j-1] + 1/3 * dp[i-1][j-1]

然后用划一下式子移个项会整出这么个奇葩的东西。。


然后就可以愉快地暴力算了。。。

#include <cstdio>#include <iostream>using namespace std;int read(){int sign = 1, n = 0; char c = getchar();while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }return sign*n;}int N, M;double dp[1005][1005];double mul(double a, int b){double temp = 1.0, cmp = a;while(b){if(b & 1) temp *= cmp;b >>= 1;cmp *= cmp;}return temp;}int main(){freopen("game.in", "r", stdin);freopen("game.out", "w", stdout);N = read(); M = read();dp[1][1] = 1.0;for(int i = 2; i <= N; ++i){double A = 1 - mul(0.5, i), B = 1.0 / 6;for(int k = 1; k < i; ++k) B += mul(0.5, k) / 3 * dp[i - 1][i - k];dp[i][1] = B / A;for(int j = 2; j <= i; ++j) dp[i][j] = 0.5 * dp[i][j - 1] + 1.0 / 3 * dp[i - 1][j - 1];}printf("%.9lf", dp[N][M]);return 0;}

3、轻音乐同好会

【题目描述】

雪菜为了能让冬马参加轻音乐同好会,瞒着春希,和冬马见面。为了增进感情,雪菜
拉着还没缓过神来的冬马进了游戏厅……
游戏要求两名玩家在排成一排的石头上跳跃前进,每个石头有一个高度,玩家只能向
右跳,并且不能跳向比自己所在位置的石头矮的石头。一个石头在一个玩家跳离后就会消
失,并且两个玩家不能同时站在同一个石头上。游戏分数为两个玩家站过的石头的总数。
游戏起始,两名玩家都可以任选一个石头作为开始位置(当然不能相同)。
由于冬马是挂科专家,雪菜又只有英语好,所以她们两人想请你帮助他们,怎么才能
让分数最高。

【输入】

第一行一个整数n,表示有n个石头。
第二行n个整数,表示从左到右第i个石头的高度Hi。

【输出】

一个整数,表示最高能得到的分数。

【样例输入】

5
1 7 3 2 4

【样例输出】

4

【数据范围】

30%的数据,2≤n≤200
100%的数据,2≤n≤1000,Hi≤10^9

题解

dp[u][v] 表示一个从i结尾一个从j结尾的最大分数(显然dp[u][v] = dp[v][u])

于是
dp[a[i]][v] = max(dp[u][v]) + 1  (a[i] >= u)   
dp[u][a[i]] = max(dp[u][v]) + 1  (a[i] >= v)

于是对于每个a[i]我们可以找到一个u(或v,其实是等效的) 在a[i] >= u(或v)的条件下 dp[u][a[i]](或dp[a[i]][v])最大
强行转移似乎是n^3的 于是我们可以考虑用线段树或者树状数组来维护
其实我也是今天才知道原来树状数组可以用来维护最大值???orz越来越觉得树状数组高端了。。

先离散化一下(stl就行了 std写的是手工离散化 我只能orz 之前手工离散化每次都把我恶心到) 
然后树状数组下标为a[i]对应的离散化的值 树状数组的值为当前区间的最大值 然后更新的时候向后更新 查找的时候向前查找就可以了
之前lower_bound的时候又手抖把M打成N了 唉毕竟是手残的人生啊

#include <cstdio>#include <iostream>#include <algorithm>#define lowbit(i) (i & -i)using namespace std;int read(){int sign = 1, n = 0; char c = getchar();while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }return sign*n;}int N, M;int bfr[1005], aft[1005], f[1005];struct BIT{int dp[1005][1005];inline int query(int *array, int i){int res = 0;while(i){res = max(res, array[i]);i -= lowbit(i);}return res;}inline void update(int *array, int i, int x){while(i <= M){array[i] = max(array[i], x);i += lowbit(i);}}}t;int main(){freopen("music.in", "r", stdin);freopen("music.out", "w", stdout);N = read();for(int i = 0; i < N; ++i){bfr[i] = read(); aft[i] = bfr[i];}sort(aft, aft + N);M = unique(aft, aft + N) - aft;for(int i = 0; i < N; ++i){int pos = lower_bound(aft, aft + M, bfr[i]) - aft + 1;for(int j = 1; j <= M; ++j) f[j] = t.query(t.dp[j], pos) + 1;for(int j = 1; j <= M; ++j){t.update(t.dp[j], pos, f[j]);t.update(t.dp[pos], j, f[j]);}}int ans = -1;for(int i = 1; i <= M; ++i) ans = max(ans, t.query(t.dp[i], M));printf("%d\n", ans);return 0;}

然后我写的费用流可以卡过18组 都能在1s内跑完。。只有两组时间是12s…………………………orz

#include <cstdio>#include <iostream>#include <cstring>#include <queue>using namespace std;int read(){int sign = 1, n = 0; char c = getchar();while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }return sign*n;}const int Nmax = 1005;const int Mmax = Nmax * Nmax;const int inf = 0x3f3f3f3f; int N, H[Nmax]; int S, T, SS;struct ed{int v, w, flow, next;}e[Mmax + 7 * Nmax];int k, head[Nmax * 2], cur[Nmax * 2];inline int in(int n) { return n; }inline int out(int n) { return n + N;}inline void adde(int u, int v, int w, int flow){//printf("%d → %d : %d\n", u, v, flow);e[k] = (ed) { v, w, flow, head[u] };head[u] = k++;e[k] = (ed) { u, -w, 0, head[v] };head[v] = k++; }bool vis[Nmax * 2];int dis[Nmax * 2], pre[Nmax * 2], minf;queue <int> q;bool spfa(){memset(dis, -0x3f, sizeof(dis));minf = inf; dis[S] = 0; q.push(S);while(q.size()){int u = q.front(); q.pop(); vis[u] = 0;for(int i = head[u]; ~i; i = e[i].next){int v = e[i].v;if(e[i].flow > 0 && dis[v] < dis[u] + e[i].w){dis[v] = dis[u] + e[i].w;minf = min(minf, e[i].flow);pre[v] = i;if(!vis[v]){ q.push(v); vis[v] = 1; }}}}return dis[T] > 0;}inline int max_flow(){int ans = 0;while(spfa()){for(int i = T; i != S; i = e[pre[i] ^ 1].v){e[pre[i]].flow -= minf;e[pre[i] ^ 1].flow += minf;}ans += dis[T] * minf;}return ans;}int main(){freopen("music.in", "r", stdin);freopen("music.out", "w", stdout);N = read(); memset(head, -1, sizeof(head));S = 0, T = (N << 1) + 1, SS = T + 1; adde(S, SS, 0, 2);for(int i = 1; i <= N; ++i){H[i] = read();adde(SS, in(i), 0, inf); adde(out(i), T, 0, inf); adde(in(i), out(i), 1, 1);for(int j = 1; j < i; ++j){if(H[j] <= H[i]) adde(out(j), in(i), 0, 1);}}printf("%d\n", max_flow());return 0;}


0 0