拿到题我觉得是两道数论加一道网络流。结果是三道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。【输出】
输出只有一个数,表示最多能删除多少数。【样例输入】
34 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个小朋友排成一排,其中Analysis排在第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。
【输出】
一个整数,表示最高能得到的分数。【样例输入】
51 7 3 2 4
【样例输出】
4【数据范围】
30%的数据,2≤n≤200100%的数据,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;
}