给定一个逆波兰表达式,要求添加若干个操作数或者运算符,或者调换任意两个运算符,使得其合法,输出最小步数。
主要思想:分类讨论。
①、当数字大于运算符的时候,可以知道并不需要添加任何数字了,这个时候只考虑交换
②、当数字小于运算符的时候,就一定是要添加数字的。
可以贪心知道,要交换的话,肯定是和最后一个字符交换是最优的。
样例有
***123 ans = 3
1*1 ans = 1
1*1* ans = 1
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; const int maxn = 1e5 + 20; char str[maxn]; vector<int> vc; void work() { vc.clear(); scanf("%s", str + 1); int lenstr = strlen(str + 1); int one = 0, two = 0; for (int i = 1; i <= lenstr; ++i) { two += str[i] == '*'; if (str[i] != '0') vc.push_back(i); } one = lenstr - two; int need = two - one + 1; int ans = 0, has = 0; for (int i = 1; i <= lenstr; ++i) { if (str[i] != '*') { has++; } else { if (has >= 2) { has--; continue; } if (has == 0) { if (need >= 2) { need -= 2; ans += 2; has = 1; } else { int pos = -1; if (vc.size()) { pos = vc.back(); vc.pop_back(); } swap(str[i], str[pos]); has = 1; ans++; } } else { if (need >= 1) { need--; ans++; } else { int pos = -1; if (vc.size()) { pos = vc.back(); vc.pop_back(); } swap(str[i], str[pos]); has = 2; ans++; } } } } cout << ans << endl; } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif int t; scanf("%d", &t); while (t--) work(); return 0; }
https://vjudge.net/contest/171196#problem/H
给定n个菜,需要使得(重量最小的 / 重量最大的)> T
询问最小需要切多少刀,保证答案 <= 500
首先我们可以暴力知道 min / max > T,
那么每次应该都是切max那个,这样是最优的。因为不可能切min的,这样只会产生一个更小的。使得new_min / max更小
那么假设不均分地切成了a[1]和a[2](并且a[2] > a[1]不失一般性),则a[1] + a[2] = mx,假设均分地切成了val,则val + val = mx
假设最小值是mi,则我们目标是取mi / a[2],和mi / val的最大者,可以知道均分切最优。因为val < a[2]
所以每次都应该均分地切。
假设每个白菜被切的次数是x[i],那么再切一次这个白菜的时候产新的新值是num[i] / x[i]
所以维护一个set,维护的是当前白菜的值,然后记录每个白菜被切的次数,一旦需要增加次数,则是重新切的。
因为比如是4000,切一次会产生2000、2000,切2次是产生1333.33 、 1333.33、 1333.33
相当于又重新切了。
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; struct Node { double pre, now; int cnt; bool operator < (const struct Node & rhs) const { return now < rhs.now; } Node(double _pre, double _now, int _cnt) { pre = _pre, now = _now, cnt = _cnt; } }; multiset<Node> ss; void work() { double t; int n; cin >> t >> n; for (int i = 1; i <= n; ++i) { double x; cin >> x; ss.insert(Node(x, x, 2)); } multiset<Node> :: iterator it1, it2; it1 = ss.begin(); it2 = ss.end(); it2--; int ans = 0; while (it1->now / it2->now <= t) { int cnt = it2->cnt; ss.insert(Node(it2->pre, it2->pre / cnt, cnt + 1)); ss.erase(it2); ans++; it1 = ss.begin(); it2 = ss.end(); it2--; } cout << ans << endl; } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif work(); return 0; }
http://acm.hdu.edu.cn/showproblem.php?pid=5521
给定m个集合,每个集合中的点相互到达的距离是一样的,都是ti,问点1和点n会面的最短时间,点1和点n同时动。
思路,直接建边是不可做的,由于集合内的相互两个元素都是满足最短路了,那么我们想另外一个东西代替这种性质。
每个集合虚拟一个节点new,对于每一个集合的点,我们要做的是x和new连接一条费用为w的边,new和x连接一条费用是0的边
那么就可以表达出集合内相互最短路的这个特性了,复杂度是O 2n的建边
然后跑一次最短路即可,好像spfa比dij慢
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; const int maxn = 2e6 + 20; struct Edge { int u, v, w, tonext; }e[maxn * 2]; int first[maxn], num; void addEdge(int u, int v, int w) { e[num].u = u, e[num].v = v, e[num].w = w, e[num].tonext = first[u]; first[u] = num++; } int tim[maxn], in[maxn]; LL dis[2][maxn], INF = 1e16; bool spfa(int bx, int n, LL dis[]) { //从bx开始,有n个顶点 for (int i = 1; i <= n; ++i) { dis[i] = 1e16; tim[i] = 0; //入队次数清0 in[i] = false; //当前这个节点不在队列里 } queue<int> que; while (!que.empty()) que.pop(); que.push(bx), in[bx] = true, dis[bx] = 0, tim[bx]++; while (!que.empty()) { int u = que.front(); if (tim[u] > n) return true; //入队次数超过n次,出现负环 que.pop(); //in[u] = false ? for (int i = first[u]; ~i; i = e[i].tonext) { if (dis[e[i].v] > dis[e[i].u] + e[i].w) { dis[e[i].v] = dis[e[i].u] + e[i].w; if (!in[e[i].v]) { //不在队列 que.push(e[i].v); in[e[i].v] = true; tim[e[i].v]++; } } } in[u] = false; } return false; } int f = 0; vector<int> vc; struct HeapNode { int u; LL dis; //dis是到起始点bx的距离 HeapNode(int from, LL cost) : u(from), dis(cost) {} bool operator < (const HeapNode &rhs) const { return dis > rhs.dis; //注意,这里的dis小的在前。 } }; int book[maxn]; void dij(int bx, LL dis[], int n) { memset (book, 0, sizeof book); // 这些数组只能放在外面, for (int i = 1; i <= n; ++i) dis[i] = INF; dis[bx] = 0; priority_queue<HeapNode> que; que.push(HeapNode(bx, dis[bx])); while(!que.empty()) { HeapNode t = que.top(); que.pop(); int u = t.u; //现在选出的这个u,是dis[]中最小的那个值 if (book[u]) continue; book[u] = true; for (int i = first[u]; ~i; i = e[i].tonext) { int v = e[i].v; if (!book[v] && dis[v] > dis[u] + e[i].w) { //找过的点再用也不行的 dis[v] = dis[u] + e[i].w; //松弛 que.push(HeapNode(v, dis[v])); } } } return ; } void work() { printf("Case #%d:", ++f); num = 0; memset(first, -1, sizeof first); int n, m; scanf("%d%d", &n, &m); int to = n + 1; for (int i = 1; i <= m; ++i) { int w, has; scanf("%d%d", &w, &has); while (has--) { int x; scanf("%d", &x); addEdge(x, to, w); addEdge(to, x, 0); } to++; } to--; dij(1, dis[0], to); dij(n, dis[1], to); if (dis[0][n] == INF) { printf(" Evil John "); return; } LL ans = INF; for (int i = 1; i <= n; ++i) ans = min(ans, max(dis[0][i], dis[1][i])); printf(" %I64d ", ans); vc.clear(); for (int i = 1; i <= n; ++i) { if (max(dis[0][i], dis[1][i]) == ans) vc.push_back(i); } sort(vc.begin(), vc.end()); for (int i = 0; i < vc.size(); ++i) { if (i == vc.size() - 1) printf("%d ", vc[i]); else printf("%d ", vc[i]); } } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif int t; scanf("%d", &t); while (t--) work(); return 0; }
B. Arpa and an exam about geometry
给定三个点,然后你可以选定一个点,旋转一个任意角度,要求点a转去点b,点b转去点c
一开始,用正交变化
然后列公式化简,最后居然解出一个合法解,但是算出来是两个点的。不知道为何,可能联立的时候,我把点消去了。
但是为什么解出来会有一个合法解,却是不同的两个点。样例二就是这种情况。
正解应该是:
①、所有三角形都有外接圆,圆心在中垂线交点上
②、这样的旋转很明显走动了一条弧,在一个圆上动的了,那么转动相同的角度,所走过的弧长是一样长的,因为半径一样。那么如果使得a到达b,b到达c,那么ab == bc要成立
然后注意的是同一直线上木有圆,不行
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; LL x[3][3]; LL dis(LL x1, LL y1, LL x2, LL y2) { return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); } void work() { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 2; ++j) { scanf("%I64d", &x[i][j]); } } if ((x[1][1] - x[0][1]) * (x[2][0] - x[0][0]) == (x[1][0] - x[0][0]) * (x[2][1] - x[0][1])) { printf("No "); return; } if (dis(x[1][0], x[1][1], x[0][0], x[0][1]) == dis(x[1][0], x[1][1], x[2][0], x[2][1])) { printf("Yes "); } else printf("No "); } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif work(); return 0; }