2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) 【solved:9 / 12】
来源:互联网 发布:手机订车票软件 编辑:程序博客网 时间:2024/06/05 07:32
A - Alphabet (LIS)
题意给你一个字符串,问你最少添加几个字符使得它能够存在一个子序列“abcdefg…xyz”。长度不超过50。
思路:26-LIS。
#include <bits/stdc++.h>using namespace std;int dp[1005];int main(){ string s; cin >> s; memset(dp, 0x3f3f3f3f, sizeof(dp)); for(int i = 0; i < s.size(); i++) *lower_bound(dp, dp + s.size(), s[i]) = s[i]; cout << 26-(lower_bound(dp, dp + s.size(), 0x3f3f3f3f) - dp) << endl; return 0;}
B - Buggy Robot (bfs)
题意:大概就是你写一个up,down,left,right的指令,操控小机器人走出迷宫,然后如果机器人遇到的这条指令,是让它走到障碍物上的,它会跳过这条指令。如果机器人到达了终点,则所有剩余指令失效。你可以通过增加和删除指令,来使得机器人走到中间,问你至少需要修改几个指令。迷宫的大小50*50。指令的长度是50。
思路:dp[i][j][k]:(i, j)这个位置 下一步执行原指令的第k条的时候 需要增加的最少指令数。然后转移一下就行了。用bfs。
#include <bits/stdc++.h>using namespace std;const int maxn = 50 + 5;const int INF = 0x3f3f3f3f;int n, m, sx, sy;struct node{int x, y, k;};int dx[] = {0, 0, -1, 1};int dy[] = {-1, 1, 0, 0};bool isInside(int x, int y){return 0 <= x && x < n && 0 <= y && y < m;}int dp[maxn][maxn][maxn];char ma[maxn][maxn];char cmd[maxn];int dir[200];int cmdLen;int bfs(){ int ret = INF; memset(dp, INF, sizeof(dp)); dp[sx][sy][0] = 0; queue<node>que; que.push({sx,sy,0}); while(que.size()) { node cur = que.front();que.pop(); if(ma[cur.x][cur.y] == 'E') { ret = min(ret, dp[cur.x][cur.y][cur.k]); continue; } for(int i = 0; i < 4; i++) { int fx = cur.x + dx[i], fy = cur.y + dy[i]; if(!isInside(fx, fy) || ma[fx][fy] == '#') { if(dir[cmd[cur.k]] == i && dp[cur.x][cur.y][cur.k + 1] > dp[cur.x][cur.y][cur.k]) { if(dp[cur.x][cur.y][cur.k + 1] == INF) que.push({cur.x, cur.y, cur.k+1}); dp[cur.x][cur.y][cur.k + 1] = dp[cur.x][cur.y][cur.k]; } continue; } int fk = cur.k, ans = dp[cur.x][cur.y][cur.k]; if(dir[cmd[cur.k]] == i) fk++; else ans++; if(dp[fx][fy][fk] > ans) { if(dp[fx][fy][fk] == INF) que.push({fx, fy, fk}); dp[fx][fy][fk] = ans; } } } return ret;}int main(){ memset(dir, -1, sizeof(dir)); dir['L'] = 0, dir['R'] = 1, dir['U'] = 2, dir['D'] = 3; scanf("%d%d", &n, &m); for(int i = 0; i < n; i++) { scanf("%s", ma[i]); for(int j = 0; j < m; j++) { if(ma[i][j] == 'R') sx = i, sy = j; } } scanf("%s", cmd); printf("%d\n", bfs()); return 0;}
C - Cameras (贪心)
题意:数轴上1-n,你已经在其中k个位置有了标记,问你至少需要添加几个标记,使得任意连续r个位置,都至少有diff = 2个标记。
2≤n≤100000,0≤k≤n,2≤r≤n
思路:先把[1,r]区间填充的满足条件,然后每次挪动其实只要更改第一个和最后一个就行了,你们觉不觉得其实这个diff这个不是2,是3是4是5都行呀,只要<=r。这题可以随便改啊,这样O(n)就够了。
#include<bits/stdc++.h>using namespace std;const int diff = 2;int vis[100000 + 5];int main(){ int n, k, r; scanf("%d%d%d", &n, &k, &r); for(int i = 0; i < k; i++) { int x; scanf("%d", &x); vis[x] = 1; } int num = 0, ans = 0; for(int i = 1; i <= r; i++) { num += vis[i]; } ans += max(0, diff - num); for(int i = r; i >= 1; i --) { if(num == diff) break; if(!vis[i]) vis[i] = 1, num++; } //接下来可以保证每一个进入的区间[l,r]的前一个[l-1,r-1]都是满足diff这个条件的。因为每次只挪动1,所以只需要更改最后一个。 for(int nail = r + 1; nail <= n; nail++) { num = num - vis[nail - r] + vis[nail]; if(num < diff) vis[nail] = 1, ans++, num = diff; } printf("%d\n", ans); return 0;}
F - Illumination(2-SAT)
题意:给你一个n*n的方格,里面有l个方格有灯,每个灯可以选择左右发射长度r格的光或者上下发射,问你是否有一种方案,使得每个格子,不会被同为纵向(横向)的多束光照射。
思路:2-SAT模板题
#include <bits/stdc++.h>using namespace std;const int maxn = 2000 + 5;struct TwoSat{//2i为假 2i+1为真 int n; vector<int>G[maxn * 2]; bool mark[maxn * 2]; int S[maxn * 2], c; void init(int n) { this->n = n; for(int i = 0; i < n * 2; i++) G[i].clear(), mark[i] = 0; } bool dfs(int x) { if(mark[x^1]) return false; if(mark[x]) return true; mark[x] = true; S[c++] = x; for(auto o : G[x]) if(!dfs(o)) return false; return true; } void add_clause(int x, int xval, int y, int yval) { x = x * 2 + xval; y = y * 2 + yval; G[x^1].push_back(y); G[y^1].push_back(x); } bool solve() { for(int i = 0; i < n * 2; i += 2) { if(!mark[i] && !mark[i + 1]) { c = 0; if(!dfs(i)) { while(c > 0) mark[S[--c]] = false; if(!dfs(i + 1)) return false; } } } return true; }}solver;struct node{int x, y;}nodes[maxn];int main(){ int n, r, l; scanf("%d%d%d", &n, &r, &l); for(int i = 0, x, y; i < l; i++) { scanf("%d%d", &x, &y); nodes[i] = {x, y}; } solver.init(l); for(int i = 0; i < l; i++) { for(int j = i + 1; j < l; j++) { if(nodes[i].x == nodes[j].x && abs(nodes[i].y - nodes[j].y) <= r) { solver.add_clause(i, 0, j, 0); } if(nodes[i].y == nodes[j].y && abs(nodes[i].x - nodes[j].x) <= r) { solver.add_clause(i, 1, j, 1); } } } if(solver.solve()) puts("YES"); else puts("NO"); return 0;}
G - Maximum Islands(最小顶点覆盖)
题意:给你一个50*50 的图,里面有LWC三种字符,L代表陆地,W代表水,C代表可以由你决定是水还是陆地,问你这个图中L的联通块最多有几块。
思路:首先很容易想到把所有L联通块周围围上W,剩下的就是C了,然后我们可以发现要使得L的联通块最多,那么就尽量每个联通块只占用给一个点,那么就变成了剩下的C连成的图,划分成两个点集,然后成为一个最小顶点覆盖问题,由于是二分图,所有转换成二分最大匹配,无向图的二分匹配随便搞搞。
#include <bits/stdc++.h>using namespace std;const int maxn = 100 + 5;vector<int>G[2500 + 5];int used[2500 + 5], match[2500 + 5];bool dfs(int v){ used[v] = 1; for(int i = 0; i < G[v].size(); i++) { int u = G[v][i], w = match[u]; if(w < 0 || !used[w] && dfs(w)) { match[v] = u; match[u] = v; return true; } } return false;}int hungary(int V){ int res = 0; memset(match, -1, sizeof(match)); for(int u = 1; u <= V; u++) { if(match[u] < 0) { memset(used, 0, sizeof(used)); if(dfs(u)) res++; } } return res;}int n, m;int dx[] = {0, 0, -1, 1};int dy[] = {1, -1, 0, 0};int vis[maxn][maxn];char ma[maxn][maxn];void dfs(int x, int y){ vis[x][y] = 1; for(int i = 0; i < 4; i++) { int fx = dx[i] + x; int fy = dy[i] + y; if(0 <= fx && fx < n && 0 <= fy && fy < m) { if(ma[fx][fy] == 'C') ma[fx][fy] = 'W'; else if(ma[fx][fy] == 'L' && vis[fx][fy] == 0) dfs(fx, fy); } }}int main(){ cin >> n >> m; for(int i = 0; i < n; i++) cin >> ma[i]; int ans = 0; for(int i = 0; i < n; i++) { for(int j = 0; j < m; j++) { if(ma[i][j] == 'L' && vis[i][j] == 0) { dfs(i, j), ans++; } } } int id = 1; map<pair<int,int>, int>rec; for(int i = 0; i < n; i++) { for(int j = 0; j < m; j++) { if(ma[i][j] == 'C') { if(rec[{i,j}] == 0) rec[{i,j}] = id++; for(int k = 0; k < 4; k++) { int fx = i + dx[k], fy = j + dy[k]; if(0 <= fx && fx < n && 0 <= fy && fy < m) { if(ma[fx][fy] == 'C') { if(rec[{fx,fy}] == 0) rec[{fx, fy}] = id++; int idx1 = rec[{i,j}], idx2 = rec[{fx, fy}]; G[idx1].push_back(idx2); G[idx2].push_back(idx1); } } } } } } int vs = id - 1; if(vs) ans = ans + vs - hungary(vs); cout << ans << endl; return 0;}
H - Paint (dp)
题意:给你20w条线段,问你选其中若干条两两不相互覆盖的线段,最多能覆盖1-n这个区间中多少个点。
思路:离散化以后,做成点,dp[i]:前i个点最多能覆盖多少。
#include <bits/stdc++.h>using namespace std;typedef long long LL;vector<LL>point;vector<pair<LL, LL>>seg;vector<LL>G[400000 + 5];LL dp[400000 + 5];int main(){ LL n, k; scanf("%lld%lld", &n, &k); for(int i = 0; i < k; i++) { LL l, r; scanf("%lld%lld", &l, &r); r++; point.push_back(l); point.push_back(r); seg.push_back({l, r}); } sort(point.begin(), point.end()); point.resize(unique(point.begin(), point.end()) - point.begin()); for(int i = 0; i < k; i++) { LL lb = seg[i].first, rb = seg[i].second; lb = lower_bound(point.begin(), point.end(), lb) - point.begin(); rb = lower_bound(point.begin(), point.end(), rb) - point.begin(); G[rb].push_back(lb); } dp[0] = 0; for(int i = 1; i < point.size(); i++) { dp[i] = dp[i - 1]; for(auto o : G[i]) { dp[i] = max(dp[i], dp[o] + point[i] - point[o]); } } printf("%lld\n", n - dp[point.size()-1]); return 0;}
I - Postman(贪心)
题意:有一个邮差员要去n家送信,他每次只能带k封信。每一家的坐标为xi,需要送mi封信,然后邮局在0点,问你最少走多少路能送完信。
(n≤1e3),(k,xi,mi≤1e7)
思路:
在纸上画一画,可以大概的发现,如果某次邮递员跨越了0点,那么和重新出发没有区别,哦不对,重新出发应该更优,能带更多的信。所以可以把问题拆分成两个独立的部分,解决正半轴以后,负半轴同理。
正半轴怎么办呢,明显是由远及近的送,然后递减就好了。
#include <bits/stdc++.h>using namespace std;struct node{ int x, m; bool operator < (const node &other)const { if(x != other.x) return x < other.x; return m < other.m; }};int main(){ int n, k; scanf("%d%d", &n, &k); priority_queue<node>que1, que2; for(int i = 0, x, m; i < n; i++) { scanf("%d%d", &x, &m); if(x < 0) que2.push({-x, m}); else que1.push({x, m}); } long long ans = 0; while(que1.size()) { node cur = que1.top();que1.pop(); long long times = (cur.m + k - 1) / k; long long Left = times * k - cur.m; ans += 2LL * cur.x * times; while(que1.size() && Left) { node temp = que1.top();que1.pop(); if(temp.m <= Left) Left -= temp.m; else if(temp.m > Left) { temp.m -= Left; Left = 0; que1.push(temp); } } } while(que2.size()) { node cur = que2.top();que2.pop(); long long times = (cur.m + k - 1) / k; long long Left = times * k - cur.m; ans += 2LL * cur.x * times; while(que2.size() && Left) { node temp = que2.top();que2.pop(); if(temp.m <= Left) Left -= temp.m; else if(temp.m > Left) { temp.m -= Left; Left = 0; que2.push(temp); } } } printf("%lld\n", ans); return 0;}
J - Shopping(RMQ+二分)
题意:给了n件商品(
n≤2e5 ),接下来有q个客户(q≤2e5 ),每个客户有一个金钱value(value≤1e18 ),和购买区间。它会从左到右挨个购买商品,一直尽可能的买每个商品,问最后剩下多少钱。
思路:因为可以考虑到%的特殊性,最多只会执行log次,所以直接用rmq维护一个区间最小值,然后二分找到右边第一个小于当前值的位置即可。
#include <bits/stdc++.h>using namespace std;const int maxn = 200000 + 5;long long a[maxn];long long d[maxn][20];void initRMQ(int n, long long a[]){ for(int i = 0; i < n; i++) d[i][0] = a[i]; for(int j = 1; (1 << j) <= n; j++) { for(int i = 0; i + (1 << j) - 1 < n; i++) { d[i][j] = min(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]); } }}long long query(int l, int r){ int k = 0; while((1 << (k + 1)) <= r - l + 1) k++; return min(d[l][k], d[r - (1 << k) + 1][k]);}long long solve(long long v, int ql, int qr){ while(v) { int lb = ql, rb = qr; if(query(lb, rb) > v) return v; while(lb < rb) { int mid = (lb + rb) / 2; if(query(lb, mid) <= v) rb = mid; else lb = mid + 1; } v %= a[rb]; ql = rb + 1; if(ql > qr) return v; } return v;}int main(){ int n, q; scanf("%d%d", &n, &q); for(int i = 0; i < n; i++) scanf("%lld", &a[i]); initRMQ(n, a); while(q--) { long long v; int ql, qr; scanf("%lld%d%d", &v, &ql, &qr); ql--, qr--; printf("%lld\n", solve(v, ql, qr)); } return 0;}
- 2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) 【solved:9 / 12】
- 2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 2)【solved:12 / 13】
- 2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 2)
- 2016-2017 ACM-ICPC Pacific Northwest Regional Contest
- 2017/9/23周测(CF2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 2))
- 2015-2016 ACM-ICPC Pacific Northwest Regional Contest Div.2 全部题目题解
- 2015-2016 ACM-ICPC Pacific Northwest Regional Contest Div.2( Problem V Gears)
- 【2015-2016 ACM-ICPC Pacific Northwest Regional Contest (Div 1)C】【排序 模拟】Classy 课程难度排序超多关键字
- 【2015-2016 ACM-ICPC Pacific Northwest Regional Contest (Div 1)D】【水题】Triangle 两个三角形是否可以恰好拼成矩形
- 【2015-2016 ACM-ICPC Pacific Northwest Regional Contest (Div 1)H】【迭代 排序 模拟】Hilbert Sort 图形拐拐划分,经过所有点的
- 【2015-2016 ACM-ICPC Pacific Northwest Regional Contest (Div 1)E】【水题 贪心】Excellence n个数两两组合使得最小和尽可能大
- 【2015-2016 ACM-ICPC Pacific Northwest Regional Contest (Div 1)A】【floyd 最小路径覆盖】最少飞机数满足所有航班要求
- 【2015-2016 ACM-ICPC Pacific Northwest Regional Contest (Div 1)G】【坐标轴变换 LIS】Racing Gems 开车吃宝石,横向速度不能超
- Codeforce Gym 100819P : 2015-2016 ACM-ICPC Pacific Northwest Regional Contest - P 仔细看题啊!
- Codeforce Gym 100819L : 2015-2016 ACM-ICPC Pacific Northwest Regional Contest - L 这是一道阅读理解(微笑)
- [CF Gym 100827C] Containment [2014-2015 ACM-ICPC Pacific Northwest Regional Contest C]
- [CF Gym 100827E] Hill Number [2014-2015 ACM-ICPC Pacific Northwest Regional Contest E]
- [CF Gym 100827F] Knights [2014-2015 ACM-ICPC Pacific Northwest Regional Contest F]
- Android自动化大讲堂32--Instrumentation对项目的自动化测试
- Android自动化大讲堂35--UIAutomator控件捕获
- Android自动化大讲堂28--Instrumentation前世今生之分析
- Android自动化大讲堂33--Instrumentation工具反思
- Android自动化大讲堂29--Instrumentation自动化脚本开发
- 2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) 【solved:9 / 12】
- ofbiz学习——畅销产品展示
- Java集合系列(五)
- 高校三维地图校内导航系统
- 查看更多和收起的js
- C#知识点整理(1)-委托
- SpringMVC上传文件
- 线程同步基础(二)
- iOS数据存储方法大全