2015 ACM Amman Collegiate Programming Contest 题解
【题目链接】
模拟。
#include <bits/stdc++.h> using namespace std; int T; int n; struct X { string name; int num; int ti; }s[10010]; bool cmp(X&a,X&b){ if(a.num != b.num) return a.num > b.num; return a.ti < b.ti; } int main() { scanf("%d", &T); while(T--) { scanf("%d",&n); for(int i=0;i<n;i++){ cin>>s[i].name; cin>>s[i].num; cin>>s[i].ti; // cout << i << endl; } for(int i = 0; i < n; i ++) { //cout << s[i].name << " " << s[i].num << " " << s[i].ti << endl; } sort(s, s+n, cmp); cout << s[0].name<<endl; } return 0; } /* 2 3 tourist 13 567 petr 13 600 endagorion 144 19 2 tourist 7 512 bayashout 7 477 */
枚举 $x$ 和 $y$,确定z,然后区间和算一下就能算出来谁赢了。
#include <bits/stdc++.h> using namespace std; int T; int n; char s[1010]; int sum[1010][3]; int Get(int L, int R, int num) { if(R < L) return 0; return sum[R][num] - sum[L-1][num]; } int check(int x, int y, int z) { //[1,x] R //[x+1,x+y] P //[x+y+1,x+y+z] S int point1 = Get(1,x,1); int pin1 = Get(1,x,0); int point2 = Get(x+1,x+y,0); int pin2 = Get(x+1,x+y,2); int point3 = Get(x+y+1,x+y+z,2); int pin3 = Get(x+y+1,x+y+z,1); // cout << point1 << " "<< point2 << " " << point3 << endl; int point_first = point1+point2+point3; int point_second = n - point_first - pin1 - pin2-pin3; if(point_first > point_second) return 1; return 0; } int main() { scanf("%d", &T); while(T--) { scanf("%d", &n); scanf("%s", s); memset(sum, 0, sizeof sum); for(int i = 0; s[i]; i ++) { sum[i+1][0] = sum[i][0]; sum[i+1][1] = sum[i][1]; sum[i+1][2] = sum[i][2]; if(s[i] == 'R') sum[i+1][0] ++; if(s[i] == 'S') sum[i+1][1] ++; if(s[i] == 'P') sum[i+1][2] ++; } int ans = 0; int len = strlen(s); for(int x = 0; x <= len; x ++) { for(int y = 0; y <= len; y ++) { if(x + y > len) continue; int z = len - x - y; ans = ans + check(x, y, z); } } printf("%d\n", ans); } return 0; }
先把照亮的都确定一下,然后看连续的没被照亮的有几个,算一下就好了。
#include <bits/stdc++.h> using namespace std; int T,n; char s[100010]; int cal(int x) { if(x == 0) return 0; int res = x / 3; if(x % 3) res ++; return res; } int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); scanf("%s",s); int ans = 0; int len = strlen(s); for(int i = 0; s[i]; i++) { if(s[i] == '*') { if(i - 1 >= 0 && s[i-1] == '.') s[i -1] = '0'; if(i + 1 < len && s[i+1] == '.') s[i+1] = '0'; } } int num = 0; for(int i = 0; s[i]; i ++) { if(s[i] != '.') { ans = ans + cal(num); num = 0; } else { num ++; } } ans = ans + cal(num); printf("%d\n", ans); } return 0; }
$dp_i$ 表示前缀需要分割成几段,枚举上一次断开的位置即可。
#include <bits/stdc++.h> using namespace std; int T; int n, k, len; char s[1010]; int dp[1010]; int jiao[1010][1010]; int Get(int x) { if(x == -1) return 0; return dp[x]; } int main() { scanf("%d", &T); while(T--) { memset(jiao,0,sizeof jiao); scanf("%d%d",&n,&k); scanf("%s",s); len = strlen(s); for(int i = 0; i < len; i ++) { if(i + 1 < len) { if(s[i] != s[i + 1]) jiao[i][i + 1] = 1; } for(int j = i + 2; j < len; j ++) { if(s[j] != s[j - 1] && jiao[i][j - 1]) jiao[i][j] = 1; } } for(int i = 0; i < n; i ++) { for(int j = i; j < n; j ++) { // cout << i << " " << j << " " << jiao[i][j] << endl; } } dp[0] = 1; for(int i = 1; i < n; i ++) { dp[i] = 100000; for(int j = i - 1; j >= -1; j --) { // [j+1, i] if(i - j > k) continue; if(jiao[j+1][i]) continue; dp[i] = min(dp[i], Get(j) + 1); } } printf("%d\n", dp[len - 1] - 1); } return 0; }
模拟。
#include <bits/stdc++.h> using namespace std; int T; int n; int x[100010]; int main() { scanf("%d", &T); while(T--) { scanf("%d",&n); int ans = 0; int cha = 1000; for(int i=0;i<n;i++){ cin>>x[i]; cha = min(cha, 100 - x[i]); } for(int i = 0; i<n;i++) { if(x[i] + cha >= 50) ans ++; } cout << ans << endl; } return 0; }
克鲁斯卡尔执行过程中,最小生成树上最大的边权就是答案。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5+10; int T; int n,m; struct Edge { int u, v; int c; }e[maxn]; int f[maxn]; int Find(int x) { if(x != f[x]) f[x] = Find(f[x]); return f[x]; } bool cmp(Edge&a,Edge&b){ return a.c<b.c; } int main() { scanf("%d", &T); while(T--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)f[i]=i; for(int i=1;i<=m;i++){ scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].c); } sort(e+1,e+1+m,cmp); int ans = 0; for(int i=1;i<=m;i++){ int fu=Find(e[i].u); int fv=Find(e[i].v); if(fu==fv)continue; f[fu]=fv; ans=e[i].c; } printf("%d\n",ans); } return 0; } /* 2 6 7 1 2 3 2 3 3 3 1 5 3 4 4 4 5 4 4 6 3 6 5 5 3 3 1 2 1 2 3 2 3 1 3 */
状压一下,某个状态是否可行,只要看看扔掉任意一个,是否都是不可行的。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5+10; int T; int n; int s; int dp[1200]; int sum[1200]; int a[20]; int lowbit(int x) { return x & (-x); } int main() { scanf("%d",&T); while(T--) { scanf("%d%d", &n, &s); for(int i = 0; i < n; i ++) { scanf("%d", &a[i]); } for(int i = 1; i < (1 << n); i ++) { for(int j = 0; j < n; j ++) { if((1 << j) & i) { sum[i] = sum[i ^ (1 << j)] + a[j]; } } } int ans = 0; for(int i = 1; i < (1 << n); i ++) { if(sum[i] < s) continue; int ok = 1; for(int j = 0; j < n; j ++) { if((1 << j) & i) { if(sum[i ^ (1 << j)] >= s) ok = 0; } } if(ok == 0) continue; int tmp = 0; for(int j = 0; j < n; j++) { if((1 << j) & i) tmp ++; } ans = max(ans, tmp); } printf("%d\n", ans); } return 0; }
求出割边,去掉割边,每个连通块缩点,会形成一棵树,看看树的直径,最优策略就是在直径两端添加边。
#include <bits/stdc++.h> using namespace std; const int N = 2e5 + 10; int dfn[N],low[N],dfs_clock; int n, m; int h[N], to[N], nx[N], sz; int bridge[N]; int belong[N]; int block; int p1, p2; int dis1, dis2; int f[N]; vector<int> g[N]; void add(int a, int b) { to[sz] = b; nx[sz] = h[a]; h[a] = sz ++; } void tarjan(int u, int fa) { dfn[u] = low[u] = ++dfs_clock; bool flag = false; for(int i=h[u]; i != -1; i = nx[i]) { int v = to[i]; if(v==fa && !flag)//如果有重边,那么边(u,v)被存了两次,所以,如果第二次访问,就让他通过 { flag = true; continue; } if(dfn[v] == 0) tarjan(v,u); low[u] = min(low[u],low[v]); if(low[v] > dfn[u]) { //cntCut++; bridge[i] = 1; } } } void dfs(int x) { belong[x] = block; for(int i = h[x]; i != -1; i = nx[i]) { if(bridge[i]) continue; if(belong[to[i]]) continue; dfs(to[i]); } } void dfs1(int x, int y) { f[x] = 1; if(y > dis1) { dis1 = y; p1 = x; } for(int i = 0; i < g[x].size(); i ++) { if(f[g[x][i]]) continue; dfs1(g[x][i], y + 1); } } void dfs2(int x, int y) { f[x] = 1; if(y > dis2) { dis2 = y; p2 = x; } for(int i = 0; i < g[x].size(); i ++) { if(f[g[x][i]]) continue; dfs2(g[x][i], y + 1); } } int main() { int T; scanf("%d", &T); while(T--) { scanf("%d%d",&n,&m); sz = 0; dfs_clock = 0; memset(dfn, 0, sizeof dfn); memset(low,0,sizeof low); for(int i = 0; i <= n; i ++) { h[i] = -1; } for(int i=0; i<m; ++i) { int a, b; scanf("%d%d",&a,&b); add(a, b); add(b, a); } memset(bridge, 0, sizeof bridge); tarjan(1, -1); for(int i = 0; i < sz; i ++) { if(bridge[i]) bridge[i ^ 1] = 1; } /* for(int from = 1; from <= n; from ++) { for(int i = h[from]; i != - 1; i = nx[i]) { if(bridge[i]) printf("%d - %d\n", from, to[i]); } } */ memset(belong,0,sizeof belong); block = 0; for(int i = 1; i <= n; i ++) { if(belong[i] == 0) { block ++; dfs(i); } } /* for(int i = 1; i <= n; i ++) { cout << i << " " << belong[i] << endl; } */ for(int i = 1; i <= block; i ++) { g[i].clear(); } for(int from = 1; from <= n; from ++) { for(int i = h[from]; i != - 1; i = nx[i]) { if(bridge[i]) { // cout << belong[from] << " " << belong[to[i]] << endl; g[belong[from]].push_back(belong[to[i]]); g[belong[to[i]]].push_back(belong[from]); } } } memset(f,0,sizeof f); p1 = 0; dis1 = 0; dfs1(1, 0); memset(f,0,sizeof f); p2 = 0; dis2 = 0; dfs2(p1,0); printf("%d\n", block - 1 - dis2); } return 0; }
枚举答案,以及枚举最终变到哪个数字,然后验证一下是否可行。
#include <bits/stdc++.h> using namespace std; int T; char s[500]; int f[500], t[500], ci[500], sum[500]; int len; int Get(int x) { if(x < 0) return 0; return sum[x]; } int Sum(int L, int R) { return (Get(R) - Get(L - 1) + 10) % 10; } bool check(int x, int y) { for(int i = 0; i < len; i ++) { t[i] = f[i]; ci[i] = 0; sum[i] = 0; } for(int i = 0; i <= len - x; i ++) { ci[i] = 0; t[i] = (t[i] + Sum(i-x+1,i-1)) % 10; while(t[i] != y) { t[i] = (t[i] + 1) % 10; ci[i] ++; } sum[i] = (Get(i - 1) + ci[i]) % 10; } for(int i = len - x + 1; i < len; i ++) { ci[i] = 0; t[i] = (t[i] + Sum(i-x+1,i-1)) % 10; // cout << i << " " << i-x+1 << " " << i-1 << endl; sum[i] = (Get(i - 1) + ci[i]) % 10; } for(int i = 0; i< len; i++) { if(t[i] != t[0]) return 0; } return 1; } int main() { scanf("%d", &T); while (T--) { scanf("%s", s); len = strlen(s); for(int i = 0; s[i]; i ++) { f[i] = s[i] - '0'; } int ans = 1; int ok = 0; for(int i = len; i >= 1; i --) { for(int limit = 0; limit <= 9; limit ++) { if(check(i, limit)) { ans = i; ok = 1; break; } } if(ok) break; } printf("%d\n", ans); } return 0; }
贪心。人按年龄排序,糖果按个数排序,贪心拿就好了。
#include <bits/stdc++.h> using namespace std; int T; int n, m; int age[100010]; int tang[100010]; struct X { int x, y; }s[100010], t[100010]; int sz1, sz2; int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&n, &m); for(int i = 1; i <= n; i ++) { scanf("%d", &age[i]); } sort(age + 1, age + 1 + n); for(int i = 1; i <= m; i ++) { scanf("%d", &tang[i]); } sort(tang+1, tang+1+m); sz1 = 0, sz2 = 0; s[sz1].x = age[1]; s[sz1].y = 1; for(int i =2 ; i <= n;i ++) { if(age[i] != s[sz1].x) { sz1 ++; s[sz1].x = age[i]; s[sz1].y = 1; } else { s[sz1].y ++; } } for(int i = 0; i <= sz1; i ++){ // cout << s[i].x << " " << s[i].y << endl; } t[sz2].x = tang[1]; t[sz2].y = 1; for(int i = 2; i <= m; i ++) { if(tang[i] != t[sz2].x) { sz2 ++; t[sz2].x = tang[i]; t[sz2].y = 1; } else { t[sz2].y ++; } } for(int i = 0; i <= sz2; i ++){ // cout << t[i].x << " " << t[i].y << endl; } int ok = 1; int p2 = 0; for(int i = 0; i <= sz1; i ++) { while(p2 <= sz2 && t[p2].y < s[i].y) { p2 ++; } if(p2 > sz2) { ok = 0; break; } p2 ++; } if(ok) printf("YES\n"); else printf("NO\n"); } return 0; }
枚举一下就好了,注意 $a_i$ 为 $0$ 的情况。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; int T; int f[maxn]; int a[maxn]; int n,k; int main() { scanf("%d", &T); while(T--) { memset(f, 0, sizeof f); scanf("%d%d", &n, &k); for(int i =1; i <= n; i ++) { scanf("%d", &a[i]); f[a[i]] ++; } sort(a+1,a+1+n); int ok = 0; for(int i =1 ; i <= n; i ++) { if(a[i] == 0) continue; if(k % a[i]) continue; if(a[i] != k / a[i]) { if(f[a[i]] && f[k / a[i]]) { printf("%d %d\n", a[i], k / a[i]); ok = 1; break; } } else { if(f[a[i]] >= 2) { printf("%d %d\n", a[i], k / a[i]); ok = 1; break; } } } if(ok == 0) printf("-1\n"); } return 0; }
D题的加强版,可以发现能推到 $i$ 位置的是一段连续的区间,维护区间最小值即可。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; int T, n, k; char s[maxn]; int seg[maxn * 4]; int pre[maxn]; int dp[maxn]; int len; void update(int pos, int val, int l, int r, int rt) { if(l == r) { seg[rt] = val; return; } int mid = (l + r) / 2; if(pos <= mid) update(pos, val, l, mid, 2 * rt); else update(pos, val, mid + 1, r, 2 * rt + 1); seg[rt] = min(seg[2 * rt],seg[2 * rt + 1]); } int get(int L, int R, int l, int r, int rt) { if(L <= l && r <= R) { return seg[rt]; } int left = 200000; int right = 200000; int mid = (l + r) / 2; if(L <= mid) left = get(L, R, l, mid, 2 * rt); if(R > mid) right = get(L, R, mid + 1, r, 2 * rt + 1); return min(left, right); } int Qu(int L, int R, int idx) { if(L < 0) return 0; return get(L, R, 0, len - 1, 1); } int main() { scanf("%d", &T); while(T--) { memset(pre, -1, sizeof pre); scanf("%d%d", &n, &k); scanf("%s", s); len = strlen(s); pre[0] = 0; for(int i = 1; i < len; i ++) { pre[i] = i; if(s[i] != s[i - 1]) pre[i] = pre[i - 1]; } for(int i = 0; i < len; i ++) { // cout << i << " " << pre[i] << endl; } dp[0] = 1; update(0, dp[0], 0, len - 1, 1); for(int i = 1; i < len; i ++) { dp[i] = dp[i - 1] + 1; if(i - pre[i] + 1 >= k) { } else { if(i - k >= -1) dp[i] = min(dp[i - 1], Qu(i - k, pre[i] - 2, i)) + 1; else if(pre[i] - 2 >= -1) dp[i] = 1; else dp[i] = dp[i - 1] + 1; } update(i, dp[i], 0, len - 1, 1); // cout << i << " " << dp[i] << endl; } printf("%d\n", dp[len - 1] - 1); } return 0; }