NOIP模拟 10.17 单调队列 + 树形Dp + 区间Dp
2017-10-17 21:09
411 查看
烟火 (fireworks.cpp/c/pas)
【题目描述】
城镇的主干道上有n个区域,从左到右编号为1到n,每个区域之间相距1个单位距离。在节日中要放m个烟火,第i个烟火会在ti时刻的ai区域放。如果在ti时刻你所处区域为x,那么你可以获得bi - | ai - x |的快乐值。在每个单位时间你可以移动不超过d个单位距离,初始的位置是任意的,求通过移动能获得快乐值和的最大值。
【输入格式】
第一行三个整数n,m,d。
接下来m行,每行三个整数ai,bi,ti。
【输出格式】
一行,能够获得快乐值和的最大值。
【样例输入】
10 2 1
1 1000 4
9 1000 4
【样例输出】
1992
【数据范围】
对于30%的数据, n <= 100,m <= 20。
对于100%的数据,1 <= n <= 150000,1 <= m <= 300,1 <= d, ai <= n,1 <= bi, ti <= 10^9,对于i >= 2有ti >= ti-1。
设dp[i][j]为第i时段站在j位置的最大收益. 由于j可以从dp[i-1]的某一段区间转移过来, 所以可用单调队列维护. 复杂度n*m.
购物 (shopping.cpp/c/pas)
【题目描述】
商店里有n个物品,第i个物品的价格为ci元。每个物品只能买一次。商店发行了n张优惠券,每个物品各有一张优惠卷。如果使用了第i张优惠券,可以使该物品便宜di元钱,必须买商品才能够使用相对应的优惠券。第1张优惠卷可以无条件使用,但对于第i>=2张优惠卷,如果需要使用第i张优惠券,则必须先使用xi这张优惠券。
现在有b元钱,问最多能购买多少商品。
【输入格式】
第一行两个整数n,b。
接下来n行,第i行先有两个整数ci,di,如果i >= 2,紧接着有第三个整数xi。
【输出格式】
一行,一个整数表示最多购买的商品数量。
【样例输入】
8 9
4 3
8 3 1
2 1 1
4 2 2
7 2 2
3 1 2
7 3 5
2 1 3
【样例输出】
4
【数据范围】
对于30%的数据,n <= 100。
对于100%的数据,1 <= n <= 5000,1 <= b <= 10^9,1 <= di < ci <= 10^9,对于i >= 2有1<= xi < i.
树形dp f[i][j][0/1]表示当前i节点买j个物品i用不用优惠券的最小花费. 感觉n^3, 实际上是n^2的. 因为每一个节点最多更新其他所有节点. 不要被表面所蒙蔽.
括号匹配 (parenthesis.pas/cpp/c)
【题目描述】
给出长度为N的括号序列(只包含(,),[,]),问有多少种方法删掉这些括号的一个子集,使得剩下的括号序列是合法的,请注意不能全部删完。
【输入格式】
输入的第一行是一个整数N,表示序列的长度。
接下来一行N个字符,表示括号序列。
【输出格式】
一行,表示方案数模1000000007的结果。
【样例输入】
4
()[]
【样例输出】
3
【数据范围】
30%的数据保证:1 <= N <= 20。
100%的数据保证:1 <= N <= 300。
区间dp. 方案数为: 合法括号内的合法删数 * 合法括号外的合法删数. 这样就可以区间dp了.
转移显然只有两种决策,第l个括号不进行匹配(删除),转移到dp(l+1,r),和第i个括号进行匹配,
直接进行暴力枚举匹配即可。
【题目描述】
城镇的主干道上有n个区域,从左到右编号为1到n,每个区域之间相距1个单位距离。在节日中要放m个烟火,第i个烟火会在ti时刻的ai区域放。如果在ti时刻你所处区域为x,那么你可以获得bi - | ai - x |的快乐值。在每个单位时间你可以移动不超过d个单位距离,初始的位置是任意的,求通过移动能获得快乐值和的最大值。
【输入格式】
第一行三个整数n,m,d。
接下来m行,每行三个整数ai,bi,ti。
【输出格式】
一行,能够获得快乐值和的最大值。
【样例输入】
10 2 1
1 1000 4
9 1000 4
【样例输出】
1992
【数据范围】
对于30%的数据, n <= 100,m <= 20。
对于100%的数据,1 <= n <= 150000,1 <= m <= 300,1 <= d, ai <= n,1 <= bi, ti <= 10^9,对于i >= 2有ti >= ti-1。
设dp[i][j]为第i时段站在j位置的最大收益. 由于j可以从dp[i-1]的某一段区间转移过来, 所以可用单调队列维护. 复杂度n*m.
#include<stdio.h> #include<cmath> #include<cstring> #include<algorithm> using namespace std; typedef long long dnt; const int maxn = 150005; int n, m, d, last, cur, q[maxn]; dnt dp[2][maxn], a[305], b[305], tim[305], ans; inline const dnt read(){ register dnt x = 0; register char ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x; } dnt calc(int pos, int sta){ dnt ret = 0; int ti = tim[sta]; for(int i = sta; tim[i] == ti && i <= m; ++i) ret += b[i] - abs(a[i] - pos); return ret; } int main(){ freopen("fireworks.in", "r", stdin); freopen("fireworks.out", "w", stdout); n = read(), m = read(), d = read(); for(int i = 1; i <= m; ++i) a[i] = read(), b[i] = read(), tim[i] = read(); for(int i = 1; i <= m; ++i){ if(tim[i] == tim[i - 1]) continue; last = cur, cur = cur ^ 1; int h = 1, t = 0; dnt lim = 1ll * (tim[i] - tim[i - 1]) * d; if(lim > n) lim = n; for(int j = 1; j <= n; ++j){ while(h <= t && q[h] + lim < j) ++h; while(h <= t && dp[last][j] >= dp[last][q[t]]) --t; q[++t] = j; dp[cur][j] = dp[last][q[h]] + calc(j, i); } h = 1, t = 0; for(int j = n; j; --j){ while(h <= t && q[h] - lim > j) ++h; while(h <= t && dp[last][j] >= dp[last][q[t]]) --t; q[++t] = j; dp[cur][j] = max(dp[cur][j], dp[last][q[h]] + calc(j, i)); } } ans = dp[cur][1]; for(int i = 2; i <= n; ++i) ans = max(dp[cur][i], ans); printf("%I64d\n", ans); } /* 150000 2 2 1 1000 1 10000 2000 2 */
购物 (shopping.cpp/c/pas)
【题目描述】
商店里有n个物品,第i个物品的价格为ci元。每个物品只能买一次。商店发行了n张优惠券,每个物品各有一张优惠卷。如果使用了第i张优惠券,可以使该物品便宜di元钱,必须买商品才能够使用相对应的优惠券。第1张优惠卷可以无条件使用,但对于第i>=2张优惠卷,如果需要使用第i张优惠券,则必须先使用xi这张优惠券。
现在有b元钱,问最多能购买多少商品。
【输入格式】
第一行两个整数n,b。
接下来n行,第i行先有两个整数ci,di,如果i >= 2,紧接着有第三个整数xi。
【输出格式】
一行,一个整数表示最多购买的商品数量。
【样例输入】
8 9
4 3
8 3 1
2 1 1
4 2 2
7 2 2
3 1 2
7 3 5
2 1 3
【样例输出】
4
【数据范围】
对于30%的数据,n <= 100。
对于100%的数据,1 <= n <= 5000,1 <= b <= 10^9,1 <= di < ci <= 10^9,对于i >= 2有1<= xi < i.
树形dp f[i][j][0/1]表示当前i节点买j个物品i用不用优惠券的最小花费. 感觉n^3, 实际上是n^2的. 因为每一个节点最多更新其他所有节点. 不要被表面所蒙蔽.
#include<stdio.h> #include<algorithm> using namespace std; const int maxn = 5005; int n, b, num, siz[maxn]; long long f[maxn][maxn][2]; int c[maxn], d[maxn], h[maxn]; struct edge{ int nxt, v;}e[maxn]; inline void add(int u, int v){ num++;e[num].v = v; e[num].nxt = h[u]; h[u] = num;} void dfs(int u){ for(int i = 0; i <= n; ++i) f[u][i][0] = f[u][i][1] = 2e18; f[u][1][1] = c[u] - d[u]; siz[u] = 1; f[u][0][0] = 0, f[u][1][0] = c[u]; for(int p = h[u]; p; p = e[p].nxt){ int v = e[p].v; dfs(v); for(int i = siz[u]; i >= 0 ; --i) for(int j = 0; j <= siz[v]; ++j){ f[u][i + j][1] = min(f[u][i + j][1], f[u][i][1] + f[v][j][1]); f[u][i + j][1] = min(f[u][i + j][1], f[u][i][1] + f[v][j][0]); f[u][i + j][0] = min(f[u][i + j][0], f[u][i][0] + f[v][j][0]); } siz[u] += siz[v]; } } int main(){ freopen("shopping.in", "r", stdin); freopen("shopping.out", "w", stdout); int x; scanf("%d%d", &n, &b); for(int i = 1; i <= n; ++i){ scanf("%d%d", &c[i], &d[i]); if(i >= 2) scanf("%d", &x), add(x, i); } dfs(1); for(int i = siz[1]; i >= 0; --i) if(f[1][i][1] <= b || f[1][i][0] <= b){ printf("%d\n", i); break; } return 0; }
括号匹配 (parenthesis.pas/cpp/c)
【题目描述】
给出长度为N的括号序列(只包含(,),[,]),问有多少种方法删掉这些括号的一个子集,使得剩下的括号序列是合法的,请注意不能全部删完。
【输入格式】
输入的第一行是一个整数N,表示序列的长度。
接下来一行N个字符,表示括号序列。
【输出格式】
一行,表示方案数模1000000007的结果。
【样例输入】
4
()[]
【样例输出】
3
【数据范围】
30%的数据保证:1 <= N <= 20。
100%的数据保证:1 <= N <= 300。
区间dp. 方案数为: 合法括号内的合法删数 * 合法括号外的合法删数. 这样就可以区间dp了.
转移显然只有两种决策,第l个括号不进行匹配(删除),转移到dp(l+1,r),和第i个括号进行匹配,
直接进行暴力枚举匹配即可。
#include<stdio.h> #include<cstring> #define deeper(a) memset(a, -1, sizeof(a)) const int mod = 1000000007; typedef long long dnt; int n; char s[305]; dnt f[305][305]; dnt dp(int l, int r){ if(~f[l][r]) return f[l][r]; if(l >= r) return 1; dnt& ret = f[l][r]; ret = 0; ret = dp(l + 1, r); char aim; if(s[l] == ')' || s[l] == ']') return ret % mod; if(s[l] == '(') aim = ')'; if(s[l] == '[') aim = ']'; for(int i = l + 1; i <= r; ++i) if(s[i] == aim) ret = (ret + dp(l + 1, i - 1) * dp(i + 1, r)) % mod; return ret % mod; } int main(){ freopen("parenthesis.in", "r", stdin); freopen("parenthesis.out", "w", stdout); scanf("%d", &n); scanf("%s", s); deeper(f); printf("%I64d\n", (dp(0, n - 1) - 1) % mod); }
相关文章推荐
- 1999: [Noip2007]Core树网的核 树形dp 单调队列
- BZOJ_1999_[Noip2007]Core树网的核_单调队列+树形DP
- 树形dp(人品问题NOIP17提高模拟训练3)
- 【bzoj2500】【幸福的道路】【树形dp+单调队列】
- [NOIP模拟][POJ 2823][单调队列]滑动的窗户(Sliding Window)
- bzoj2500幸福的道路 树形dp+单调队列
- hdu 4123 Bob’s Race 树形dp+单调队列
- [jzoj]3482. 【NOIP2013模拟10.23】轮舞前夕(经典树形DP)
- 两种解法-树形dp+二分+单调队列(或RMQ)-hdu-4123-Bob’s Race
- HDU 4123 Bob’s Race (树形DP + 单调队列)
- POJ - 4003 Bob’s Race (树形DP+二分+单调队列+记忆化搜索)@
- jzoj3501 【NOIP2013模拟联考15】消息传递(news) 树形dp
- HDU 4123 Bob’s Race(树形DP + 单调队列)
- 【GDOI2014模拟】服务器 (斜率单调队列优化Dp)
- (vijos 1892 noip 模拟 tree)<树形DP求树的最大匹配及方案数>
- NOIP模拟dp专题 Question 单调栈
- bzoj1023 [SHOI2008]cactus仙人掌图 树形DP+单调队列
- hdu 4123 Bob’s Race(树形DP+单调队列)
- [NOIP模拟] 拆网线 树形DP
- JZOJ5390. 【NOIP2017提高A组模拟9.26】逗气 单调队列