HFUT&AHU组团训练(一)----DP专题
2014-03-24 00:36
246 查看
网址:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=42394#overview
密码:akak
A题:
题意:求两个字符串的最长公共子串,特别的是,这个子串的每块连续的部分必须大于等于题目所给的k。
思路:第一遍打时完全时一片浆糊,采用常规方法,各种细节没有考虑到,果断删了重打。既然段都要求>=k,那么我一次就先找那连续的长度k,再给这一段记录。相当于先局部,再整体。
B题:
题意:有很多俄罗斯套娃,讲其恢复原装,求最后桌子上最少有几个套娃?
思路:先将宽度由小到达排序,再讲高度从大到小排序,为了保证同宽度的只能有一个存在在一组套娃。用一个数组维护高度,每个元素代表一组套娃,值为当前该组套娃高度,数组整体是递减的。每读取一个高度,去覆盖当前能覆盖最高套娃组,若没有能覆盖的,则新建一个元素,即增加一个套娃组。最后求数组长度。
C题:
题意:有c(1<=c<=4)种颜色的牌,每种牌n(1<=n<=100)张。给出起始顺序,问最少移动几张牌,可使牌组有序,即牌从小到大,且同颜色为一组。
思路:关键在于c只有4种颜色,方便很多。枚举颜色出现的顺序,定一个序列,计算最长上升子序列s。用序列长度减去子序列长度,即是答案。
D题:
题意:有一排药水,药水有自己的颜色(a,b...),每次混合相邻药水,会产生a*b的气体,位置不变,颜色变为(a+b) mod 100.最后求生成最小体积是多少
思路:用记忆化搜索即可,无注意点。
F题:
题意:一帮人参加聚会,要求他们的美丽度和强壮度都必须同时满足大于任何一个人,或者小于任何一个人(不能及格比A强,一个比A弱)最多邀请多少人,并输出顺序(任意)
思路:最长上升序列变形,记录前驱节点即可。
I题:
题意:求一个点集最少点的数目,要求满足一棵树的任意一条边都至少有一个端点在这个点集中。
思路:开始想的二分图,结果发现很容易反正。最后使用树形dp,简单解决。
E题:
题意:潜水员需要OL升氧气和NL升氮气,有若干组气体瓶o升氧气,n升氮气,w重量。最后要求至少满足OL和NL的情况下,潜水员最轻要背多少?
思路:01背包变形,f[i][j][k]代表到第i组气体瓶时j氧气k氮气最少的重量。开始想可以用2维优化,但是发现因为状态转移方程式加法,无法改变。
密码:akak
A题:
题意:求两个字符串的最长公共子串,特别的是,这个子串的每块连续的部分必须大于等于题目所给的k。
思路:第一遍打时完全时一片浆糊,采用常规方法,各种细节没有考虑到,果断删了重打。既然段都要求>=k,那么我一次就先找那连续的长度k,再给这一段记录。相当于先局部,再整体。
#include <cstdio> #include <iostream> #include <vector> using namespace std; const int MAX_SIZE = 1010; int K; string s[2]; int v[MAX_SIZE][MAX_SIZE]; inline int common_len(int i, int j) { for (int k = 0; k < MAX_SIZE; k++) { if(i+k >= s[0].length() || j+k >= s[1].length()) return k; if(s[0][i+k] != s[1][j+k]) return k; } return -1; } inline void check(int i, int j) { const int len = common_len(i,j); v[i+1][j+1] = max(v[i+1][j+1], max(v[i][j+1], v[i+1][j])); if(len >= K) { for (int k = K; k < len+1; k++) v[i+k][j+k] = max(v[i+k][j+k], v[i][j] + k); } } int main() { for (int i = 0; i < MAX_SIZE; i++) v[0][i] = v[i][0] = 0; while(cin >> K) { if(K == 0) break; cin >> s[0]; cin >> s[1]; const int n = s[0].size(), m = s[1].size(); for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) v[i+1][j+1] = 0; for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) check(i,j); cout << v [m] << endl; } }
B题:
题意:有很多俄罗斯套娃,讲其恢复原装,求最后桌子上最少有几个套娃?
思路:先将宽度由小到达排序,再讲高度从大到小排序,为了保证同宽度的只能有一个存在在一组套娃。用一个数组维护高度,每个元素代表一组套娃,值为当前该组套娃高度,数组整体是递减的。每读取一个高度,去覆盖当前能覆盖最高套娃组,若没有能覆盖的,则新建一个元素,即增加一个套娃组。最后求数组长度。
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; struct aaa{ int w; int h; }a[40005]; int n; int t; int cmp(aaa a, aaa b){ if (a.w == b.w) return b.h < a.h; return b.w > a.w; } int d[40005]; int main(){ memset(d,1,sizeof(d)); cout << d[1] << endl; scanf("%d",&t); while(t--){ scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d%d", &a[i].w ,&a[i].h); sort(a, a+n, cmp); int len = 0; for (int i = 0; i < n; i++){ int l = 0, r = len; int m; while(l < r){ m = (l + r) >> 1; if (a[i].h <= d[m]) l = m+1; else r = m; /* if (a[i].h >= d[m]) r = m else l = m;*/ } d[l] = a[i].h; len+=l==len; } printf("%d\n",len); } }
C题:
题意:有c(1<=c<=4)种颜色的牌,每种牌n(1<=n<=100)张。给出起始顺序,问最少移动几张牌,可使牌组有序,即牌从小到大,且同颜色为一组。
思路:关键在于c只有4种颜色,方便很多。枚举颜色出现的顺序,定一个序列,计算最长上升子序列s。用序列长度减去子序列长度,即是答案。
#include<iostream> #include<string> #include<queue> #include<stack> #include<cstdio> #include<cmath> using namespace std; struct aaa{ int col; int val; }w[500]; int MAX = (1 << 31) -1; int c, n, sum =MAX; int d[500]; int a[5]={0}; bool v[500]; int q[500]; void card(){ int s = c*n; for (int i = 1; i <= s; i++){ d[i] = w[i].val * a[w[i].col]; } q[1] = d[1]; int len = 1; for (int i = 2; i <= s; i++){ if (d[i] > q[len]) q[++len] = d[i]; else{ int l = 1; int r = len; while(l <= r){ int m = (l + r) >> 1; if (q[m] < d[i]) l = m + 1; else r = m - 1; } q[l] = d[i]; } } if (sum > s - len) sum = s-len; } int main(){ scanf("%d%d", &c, &n); int s = c*n; for (int i = 1; i <= s; i++){ scanf("%d%d", &w[i].col, &w[i].val); } s = 1; for (int i = 2; i <= c; i++) s = s*i; while(s--){ for (int i = 1; i <= c; i++){ a[i] = 1; if (c > 1){ for (int j = 1; j <= c; j++) if (i != j){ a[j] = 101; if (c > 2){ for (int k = 1; k <= c; k++) if (i!=k && j!= k){ a[k] = 20101; if (c > 3){ for (int l = 1; l <= c; l++) if (i != l && j != l && k!= l){ a[l] = 4010101; card(); } }else card(); } }else card(); } }else card(); } } printf("%d\n", sum); }
D题:
题意:有一排药水,药水有自己的颜色(a,b...),每次混合相邻药水,会产生a*b的气体,位置不变,颜色变为(a+b) mod 100.最后求生成最小体积是多少
思路:用记忆化搜索即可,无注意点。
#include<iostream> #include<cstring> using namespace std; struct aaa{ int c; int v; }; aaa f[500][500]; int n; aaa dp(int l, int r){ if (f[l][r].c < 100) return f[l][r]; aaa t,mag1,mag2; t.v = (1 << 31) -1; for (int i = l; i < r; i++){ mag1 = dp(l, i); mag2 = dp(i+1, r); // cout << l << " " << i << mag1.c << endl; //cout << i+1 << " " << r << mag2.c << endl; if (mag1.c*mag2.c+ f[l][i].v + f[i+1][r].v < t.v) { t.v = mag1.c*mag2.c + f[l][i].v + f[i+1][r].v; t.c = (mag1.c+mag2.c) %100; } } f[l][r] = t; return f[l][r]; } int main(){ while(cin >> n){ for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++){ f[i][j].c = 100; f[i][j].v = 0; } for (int i = 1; i <= n; i++) cin >> f[i][i].c; aaa t = dp(1,n); cout << t.v << endl; } }
F题:
题意:一帮人参加聚会,要求他们的美丽度和强壮度都必须同时满足大于任何一个人,或者小于任何一个人(不能及格比A强,一个比A弱)最多邀请多少人,并输出顺序(任意)
思路:最长上升序列变形,记录前驱节点即可。
#include<iostream> #include<algorithm> #include<cstdio> using namespace std; struct aaa{ int s; int b; int p; bool operator < (aaa a)const { if (s == a.s) return b > a.b; return s < a.s; } }a[100010]; int d[100010]; int pos[100010]={0}; int pre[100010]={0}; int main(){ int n; scanf("%d", &n); for (int i = 1; i <= n; i++){ scanf("%d%d", &a[i].s, &a[i].b); a[i].p = i; } sort(a+1,a+n+1); int len = 1; pos[1] = 1; for (int i =2; i <= n; i++){ if (a[i].b > a[pos[len]].b){ pos[++len] = i; pre[i] = pos[len-1]; } else{ int l = 1; int r = len; while(l <= r){ int m = (l +r) >> 1; if (a[pos[m]].b < a[i].b) l = m + 1; else r = m - 1; } pos[l] = i; pre[i] = pos[l-1]; } } printf("%d\n", len); for (int i = pos[len]; i; i = pre[i]) printf("%d ",a[i].p); cout << endl; }
I题:
题意:求一个点集最少点的数目,要求满足一棵树的任意一条边都至少有一个端点在这个点集中。
思路:开始想的二分图,结果发现很容易反正。最后使用树形dp,简单解决。
#include<iostream> #include<cstdio> #include<vector> #include<cmath> #include <algorithm> using namespace std; vector<int> f[100001]; int d[100001][2]; int n,x,y; void treedp(int u, int rt){ d[u][0] = 0; d[u][1] = 1; for (int i = 0; i < f[u].size(); i++){ int temp = f[u][i]; if (f[u][i] == rt) continue; treedp(temp, u); d[u][0] += d[temp][1]; d[u][1] += min(d[temp][1], d[temp][0]); } } int main(){ scanf("%d", &n); for (int i = 0; i < n; i++) f[i].clear(); for (int i = 1; i < n; i++){ scanf("%d%d", &x, &y); f[x-1].push_back(y-1); f[y-1].push_back(x-1); } treedp(0,-1); cout << min(d[0][0], d[0][1]) << endl; }
E题:
题意:潜水员需要OL升氧气和NL升氮气,有若干组气体瓶o升氧气,n升氮气,w重量。最后要求至少满足OL和NL的情况下,潜水员最轻要背多少?
思路:01背包变形,f[i][j][k]代表到第i组气体瓶时j氧气k氮气最少的重量。开始想可以用2维优化,但是发现因为状态转移方程式加法,无法改变。
#include<iostream> #include<cstdio> #include<vector> #include<cmath> #include <algorithm> using namespace std; int test,OL,NL,n,ol,on; int f[1001][22][80]; struct aaa{ int o; int n; int w; }a[1001]; int main(){ int MAX= (1 <<31) -1; cin >> test; while(test--){ cin >> OL >> NL; cin >> n; for (int i = 1; i <= n; i++) cin >> a[i].o >> a[i].n >> a[i].w; for (int j = OL; j >= 0; j--) for (int k = NL; k >= 0; k--){ f[0][j][k] = MAX; } for (int i = 1; i <= n; i++){ for (int j = OL; j >= 0; j--) for (int k = NL; k >= 0; k--){ f[i][j][k] = f[i-1][j][k]; ol = j + a[i].o > OL ? OL : j + a[i].o; on = k + a[i].n > NL ? NL : k + a[i].n; if ( j == 0 && k == 0 && f[i][ol][on] > a[i].w) f[i][ol][on] = a[i].w; if (f[i-1][j][k] < MAX && f[i-1][j][k] + a[i].w < f[i][ol][on]) f[i][ol][on] = f[i-1][j][k] + a[i].w; else f[i][j][k] = f[i-1][j][k]; } } cout << f [OL][NL] <<endl; } }
相关文章推荐
- 概率dp && 高斯消元 专题训练
- dp暑假专题 训练记录
- 11/12训练日记 数位dp专题结束
- 专题(弱点)Dp训练总结【状压Dp*1+区间Dp*5+数位dp*3+树型Dp*2】【10/11】
- dp训练 1005
- 单调队列 && 斜率优化dp 专题
- 暑假训练2-DP专题
- dp训练 1003
- dp专题训练
- HDU 6170 && 2017 多校训练:Two strings(DP)
- dp训练 1004
- 【学习笔记&训练记录】数位DP
- 树形动态规划(树形DP)入门问题—初探 & 训练
- 斜率优化dp专题 & BZOJ1010 HNOI2008 玩具装箱toy
- SCAU_专题训练2&3
- 2016 SCUT 专题训练 简单dp
- 蒟蒻DP专题训练2--HDU1231
- 字符串 专题训练 · 最长回文子串之Manacher算法
- ACM: DP训练好题 动态规划题 poj 1…
- [暑假专题]DP&贪心 小结(更新中)