gym100712 ACM Amman Collegiate Programming Contest
非常水的手速赛,大部分题都是没有算法的。巨慢手速,老年思维。2个小时的时候看了下榜,和正常人差了3题(,最后还没写完跑去吃饭了..
A 水 Sort 比大小
/** @Date : 2017-09-01 12:32:08 * @FileName: A.cpp * @Platform: Windows * @Author : Lweleth (SoungEarlf@gmail.com) * @Link : https://github.com/ * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair<int ,int> #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e5+20; const double eps = 1e-8; struct yuu { string na; int s, p; }a[110]; int cmp(yuu a, yuu b) { if(a.s == b.s) return a.p < b.p; return a.s > b.s; } int main() { int T; cin >> T; while(T--) { int n; cin >> n; int s, p; string na; for(int i = 0; i < n; i++) { cin >> a[i].na >> a[i].s >> a[i].p; } sort(a, a + n, cmp); cout << a[0].na << endl; } return 0; }
B 水 枚举位置 猜拳,已经给定了出拳顺序,枚举石布临界,布剪临界的两个位置就好
/** @Date : 2017-09-01 13:37:22 * @FileName: B.cpp * @Platform: Windows * @Author : Lweleth (SoungEarlf@gmail.com) * @Link : https://github.com/ * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair<int ,int> #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e5+20; const double eps = 1e-8; int r[1100], s[1100], p[1100]; int n; char a[1100]; int main() { int T; cin >> T; while(T--) { int n; MMF(r); MMF(s); MMF(p); scanf("%d", &n); scanf("%s", a + 1); for(int i = 1; i <= n; i++) { if(a[i] == 'R') r[i]++; else if(a[i] == 'S') s[i]++; else p[i]++; r[i] += r[i - 1]; s[i] += s[i - 1]; p[i] += p[i - 1]; } int ans = 0; for(int i = 0; i <= n; i++) { for(int j = 0; j + i <= n; j++) { int w = s[i] - s[0] + r[i + j] - r[i] + p[n] - p[i + j]; int l = p[i] - p[0] + s[i + j] - s[i] + r[n] - r[i + j]; if(w > l) ans++; } } printf("%d\n", ans); } return 0; }
C 水 标记 一个*覆盖掉3个格子,先处理好,再扫一遍
/** @Date : 2017-09-01 12:48:52 * @FileName: C.cpp * @Platform: Windows * @Author : Lweleth (SoungEarlf@gmail.com) * @Link : https://github.com/ * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair<int ,int> #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e5+20; const double eps = 1e-8; char a[110]; int vis[110]; int main() { int T; cin >> T; while(T--) { int n; MMF(a); MMF(vis); cin >> n >> a; for(int i = 0; i < n; i++) { if(a[i] == '*') vis[i] = vis[i - 1] = vis[i + 1] = 1; } int cnt = 0; for(int i = 0; i < n; i++) { if(a[i] == '.' && vis[i] == 0) cnt++, vis[i] = vis[i + 1] = vis[i + 2] = 1; //cout << a[i]; } cout << cnt << endl; } return 0; }
D DP 求把一个01串分成均不大于k长度且不是交替串的最小次数。对于任意位置起始的串,在延伸k长度内如果不是交替串那么可以进行转移,预处理出所有区间是否是交替串,DP转移,然后最后的k长度内里选个合法的最小值就行
/** @Date : 2017-09-01 15:59:40 * @FileName: D.cpp * @Platform: Windows * @Author : Lweleth (SoungEarlf@gmail.com) * @Link : https://github.com/ * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e5+20; const double eps = 1e-8; int vis[1010][1010]; int dp[1010]; char a[1010]; int n, k; int main() { int T; cin >> T; while(T--) { scanf("%d%d", &n, &k); scanf("%s", a + 1); MMF(vis); for(int i = 1; i <= n; i++) { for(int j = i + 1; j <= n; j++) { if(a[j] == a[j - 1] || vis[i][j - 1]) vis[i][j] = 1; else vis[i][j] = 0; } vis[i][i] = 1; } MMI(dp); dp[1] = 0; for(int i = 1; i < n; i++) { for(int j = 1; j <= k; j++) { if(j + i <= n && vis[i][i + j - 1]) dp[i + j] = min(dp[i] + 1, dp[i + j]); } } int mi = INF; for(int i = n - k + 1; i <= n; i++) { if(vis[i][n]) mi = min(mi, dp[i]); } printf("%d\n", mi); } return 0; }
E 水 所有值均加上一定值使其中的最大值不超过100,问最终超过50的有多少个 之前题意还看错...英语太差
/** @Date : 2017-09-01 12:39:07 * @FileName: E.cpp * @Platform: Windows * @Author : Lweleth (SoungEarlf@gmail.com) * @Link : https://github.com/ * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair<int ,int> #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e5+20; const double eps = 1e-8; int a[110]; int main() { int T; cin >> T; while(T--) { int n; scanf("%d", &n); int ma = 0; for(int i = 0; i < n; i++) { scanf("%d", a + i); ma = max(ma, a[i]); } int cnt = 0; for(int i = 0; i < n; i++) { if(a[i] + 100 - ma >= 50) cnt++; } printf("%d\n", cnt); } return 0; }
F kruskal 给图问走遍所有点的一条路径中,求其中最大的边权值的最小值。 完全就是kruskal的过程。
/** @Date : 2017-09-01 15:08:39 * @FileName: F.cpp * @Platform: Windows * @Author : Lweleth (SoungEarlf@gmail.com) * @Link : https://github.com/ * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair<int ,int> #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e5+20; const double eps = 1e-8; /*typedef struct sion { int nxp; int dis; sion(){}; sion(int n, int d):nxp(n),dis(d){} }yuu; vector<sion>edg[N]; int dic[N]; int vis[N]; int n, m; int prim(int n) { MMI(dic); MMF(vis); for(auto i : edg[1]) dic[i.nxp] = i.dis; dic[1] = 0; vis[1] = 1; for(int i = 1; i <= n - 1; i++) { int tmp = INF, np; for(int j = 1; j <= n; j++) { if(!vis[j] && tmp > dic[j]) tmp = dic[j], np = j; } if(tmp == INF) return 0; vis[np] = 1; for(auto j: edg[np]) { if(!vis[j.nxp] && (j.dis) < dic[j.nxp]) dic[j.nxp] = (j.dis); } } int ma = 0; for(int i = 2; i <= n; i++) ma = max(dic[i], ma); return ma; }*/ struct edge { int u, v, w; }edg[N]; int n, m; int fa[N]; int find(int x) { if(x != fa[x]) fa[x] = find(fa[x]); return fa[x]; } int cmp(edge a, edge b) { return a.w < b.w; } int join(int a, int b) { int x = find(a); int y = find(b); if(x != y) { fa[y] = x; return 1; } return 0; } int main() { int T; cin >> T; while(T--) { scanf("%d%d", &n, &m); for(int i = 0; i <= n; i++) fa[i] = i; for(int i = 0; i < m; i++) { int u, v, w; scanf("%d%d%d", &edg[i].u, &edg[i].v, &edg[i].w); } sort(edg, edg + m, cmp); int cnt = 0; int ma = 0; for(int i = 0; i < m && cnt < n; i++) { if(join(edg[i].u, edg[i].v)) { cnt++; ma = edg[i].w; } } printf("%d\n", ma); } return 0; }
G 状态枚举 最多10个物品要求取物品值的和不小于s,拿掉其中最小的后的值要小于s。 开始还在DP想啊想,就10个数用什么DP阿...
/** @Date : 2017-09-01 14:31:31 * @FileName: G.cpp * @Platform: Windows * @Author : Lweleth (SoungEarlf@gmail.com) * @Link : https://github.com/ * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair<int ,int> #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e5+20; const double eps = 1e-8; int n, k; int a[1210]; int main() { int T; cin >> T; while(T--) { scanf("%d%d", &n, &k); for(int i = 0; i < n; i++) scanf("%d", a + i); int ma = 0; for(int i = 0; i < (1 << n); i++) { int s = 0; int mi = INF; int cnt = 0; for(int j = 0; j < n; j++) { if(((1 << j) & i)) s += a[j], cnt++, mi = min(mi, a[j]); } if(s >= k && s - mi < k) ma = max(cnt, ma); } printf("%d\n", ma); } return 0; }
H(补) tarjan缩点 树的直径 题目问给图上加一条边,使图上的桥的数量最小,问数量。第一次碰到比较简单可以用tarjan,趁这个机会学一下...题目的思路是先求出所有桥,然后剩下的连通块分别缩点,重新建成树。此时树上的边都是桥,最后加边的话只要找树上两点距离最大的,也就是树的直径。这样就能保证包含最多的桥。
/** @Date : 2017-09-04 21:09:38 * @FileName: H tarjan缩点 树的直径.cpp * @Platform: Windows * @Author : Lweleth (SoungEarlf@gmail.com) * @Link : https://github.com/ * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair<int ,int> #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e5+20; const double eps = 1e-8; struct sion { int to, nxt; bool flag; //is bridge }; sion edg[N*2]; int low[N], dfn[N]; int fa[N]; stack<int>stk; bool instk[N]; int bridges;//桥 int block;//边双连通块 int idx; //// int head[N], tot/*, nxt[N], isbg[N], to[N]*/; void add(int x, int y)//前向星 { edg[tot].to = y; edg[tot].nxt = head[x]; edg[tot].flag = 0; head[x] = tot++; } void init() { MMF(dfn); MMF(instk); MMG(head); idx = block = bridges = tot = 0; while(!stk.empty()) stk.pop(); } void tarjan(int x, int pre) { low[x] = dfn[x] = ++idx; stk.push(x); instk[x] = 1; for(int i = head[x]; ~i; i = edg[i].nxt) { int np = edg[i].to; if(np == pre) continue; if(!dfn[np]) { tarjan(np, x); low[x] = min(low[np], low[x]); if(low[np] > dfn[x]) { bridges++; edg[i].flag = 1; edg[i ^ 1].flag = 1;//注意双向 } } else if(instk[np]) low[x] = min(dfn[np], low[x]); } if(low[x] == dfn[x]) { int np; block++; do{ np = stk.top(); stk.pop(); instk[np] = 0; fa[np] = block; }while(np != x); } } //// int k = 0, pos = -1; int n, m; int dfs(int x, int pre, int dep) { if(k < dep) k = dep, pos = x; for(int i = head[x]; ~i; i = edg[i].nxt) { int np = edg[i].to; if(np == pre) continue; dfs(np, x, dep + 1); } } //// int xx[N], yy[N]; int main() { int T; cin >> T; while(T--) { init(); scanf("%d%d", &n, &m); for(int i = 1; i <= m; i++) { scanf("%d%d", xx + i, yy + i); add(xx[i], yy[i]); add(yy[i], xx[i]); } tarjan(1, 0); tot = 0; MMG(head); LL ans = 0; for(int i = 1; i <= m; i++) { int x = fa[xx[i]]; int y = fa[yy[i]]; if(x != y) { ans++; add(x, y); add(y, x); } } k = 0; dfs(1, -1, 0); k = 0; dfs(pos, -1, 0); printf("%lld\n", (ans - k)<0?0:ans-k); } return 0; }
I 暴力枚举 使用固定长度的区间,可以把区间的数字同时加,问最大使用多长的区间,能把所有数字变成一样 暴力枚举每一种长度, 主要就是check该长度下是否合法的写法问题
/** @Date : 2017-09-14 19:52:52 * @FileName: I 枚举 暴力.cpp * @Platform: Windows * @Author : Lweleth (SoungEarlf@gmail.com) * @Link : https://github.com/ * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair<int ,int> #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e5+20; const double eps = 1e-8; char s[500]; int check(int k) { int len = strlen(s + 1); int q[500]; for(int i = 0; i < 10; i++) { MMF(q); int cnt = 0; int flag = 0; for(int j = 1; j <= len; j++) { if(j > k) cnt = (cnt - q[j - k] + 10) % 10; if((q[j] + s[j] - '0' + cnt) % 10 != i && j + k - 1 > len) { flag = 1; break; } q[j] = (i - (q[j] + 10 + s[j] - '0' + cnt)%10 + 10) % 10; cnt = (cnt + q[j]) % 10; //cout << i<<j<< k<<endl; } if(!flag) return 1; } return 0; } int main() { int T; cin >> T; while(T--) { scanf("%s", s + 1); int x = strlen(s + 1); int ans = 1; for(int i = x; i >= 1; i--) { if(check(i)) { ans = i; break; } } printf("%d\n", ans); } return 0; }
J 贪心 离散化大小,存数量,排序,从前往后比较,如果一种糖果不够分,只能用下一种糖果,且下个年龄也要从下种糖果开始枚举
/** @Date : 2017-09-01 15:59:40 * @FileName: D.cpp * @Platform: Windows * @Author : Lweleth (SoungEarlf@gmail.com) * @Link : https://github.com/ * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e5+20; const double eps = 1e-8; int vis[1010][1010]; int dp[1010]; char a[1010]; int n, k; int main() { int T; cin >> T; while(T--) { scanf("%d%d", &n, &k); scanf("%s", a + 1); MMF(vis); for(int i = 1; i <= n; i++) { for(int j = i + 1; j <= n; j++) { if(a[j] == a[j - 1] || vis[i][j - 1]) vis[i][j] = 1; else vis[i][j] = 0; } vis[i][i] = 1; } MMI(dp); dp[1] = 0; for(int i = 1; i < n; i++) { for(int j = 1; j <= k; j++) { if(j + i <= n && vis[i][i + j - 1]) dp[i + j] = min(dp[i] + 1, dp[i + j]); } } int mi = INF; for(int i = n - k + 1; i <= n; i++) { if(vis[i][n]) mi = min(mi, dp[i]); } printf("%d\n", mi); } return 0; }
K 水 问k能不能由序列里不同下标的两个数相乘而得...
/** @Date : 2017-09-01 13:07:44 * @FileName: K.cpp * @Platform: Windows * @Author : Lweleth (SoungEarlf@gmail.com) * @Link : https://github.com/ * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair<int ,int> #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e5+20; const double eps = 1e-8; int vis[N]; int n, k; int main() { int T; cin >> T; while(T--) { scanf("%d%d", &n, &k); MMF(vis); for(int i = 0; i < n; i++) { int x; scanf("%d", &x); vis[x]++; } int ans = -1; for(int i = 1; i <= k; i++) { if(k % i == 0 && vis[i] && vis[k / i]) { if(i == k / i && vis[i] < 2) continue; ans = i; break; } } if(ans > 0) printf("%d %d\n", ans, k / ans); else printf("-1\n"); } return 0; }
L D题的加强版,普通O n^2不能满足,考虑使用单调队列优化DP