#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; int a[4]; int main(void) { int n; cin >> n; int mn1, mx1, mn2, mx2, mn3, mx3; cin >> mn1 >> mx1; cin >> mn2 >> mx2; cin >> mn3 >> mx3; a[3] = mn3; a[2] = mn2; int res = n - a[2] - a[3]; if (res <= mx1) a[1] = res; else { a[1] = mx1; res -= a[1]; if (a[2] + res <= mx2) a[2] += res; else { a[2] = mx2; a[3] += (res - (a[2] - mn2)); } } cout << a[1] << " " << a[2] << " " << a[3] << endl; return 0; }
贪心 || 二分 B - Pasha and Tea
题意:有n个girl和n个boy喝茶,茶杯的容量不等,boy喝的是girl的两倍且boy喝的一样多,girl喝的一样多,问主人最多能倒出多少水
分析:第一反应是用二分搜索girl喝的茶容量,可惜写搓了,mid应该和茶量最小的girl以及boy去比较。看过的代码竟然99%都是贪心,晕~
代码(二分):
#include <cstdio> #include <algorithm> #include <cstring> #include <iostream> using namespace std; const int N = 1e5 + 10; const int INF = 0x3f3f3f3f; const double EPS = 1e-9; int a[N*2]; int main(void) { int n; int w; scanf ("%d%d", &n, &w); n *= 2; for (int i=1; i<=n; ++i) { scanf ("%d", &a[i]); } sort (a+1, a+1+n); double l = 0.0, r = w; double mid = 0.0, ans = 0.0; for (int i=1; i<=100; ++i) { mid = (l + r) / 2.0; if (mid >= 0 && mid <= a[1] && mid * 2 <= a[n/2+1] && mid * 1.5 * n <= w) { l = mid; ans = mid * 1.5 * n; } else r = mid; } printf ("%.6f ", ans); return 0; }
代码(贪心):
#include <cstdio> #include <algorithm> #include <cstring> #include <iostream> using namespace std; const int N = 1e5 + 10; int a[N*2]; int main(void) { int n, w; scanf ("%d%d", &n, &w); n *= 2; for (int i=1; i<=n; ++i) scanf ("%d", &a[i]); sort (a+1, a+1+n); double ans = min (a[1] * 1.0, a[n/2+1] / 2.0); ans = min (ans * 3 * n / 2.0, w * 1.0); printf ("%.6f ", ans); return 0; }
构造+贪心:C - Arthur and Table
题意:简单说就是拿掉一些凳脚,使得剩下的最长的凳脚的个数比一半还多,每条凳子有拿走的费用,问最小费用是多少。
分析:先按照凳长排序,从最大的开始枚举,相同长度的跳过,那么统计还需要拿掉几条凳脚,优先拿费用小的,怎么找呢?这题很人性的将费用范围定在200内,直接暴力找啊!
收获:算不上收获,这道题不需要什么算法,脑子清晰地实现代码,构造题要思维严谨,逻辑清楚
代码:
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; const int INF = 0x3f3f3f3f; struct Leg { int l, cnt, d; bool operator < (const Leg &r) const { return l < r.l; } }leg[N]; int c[222]; int main(void) { int n; scanf ("%d", &n); for (int i=1; i<=n; ++i) { scanf ("%d", &leg[i].l); } memset (c, 0, sizeof (c)); int tot = 0; for (int i=1; i<=n; ++i) { scanf ("%d", &leg[i].d); tot += leg[i].d; c[leg[i].d]++; } sort (leg+1, leg+1+n); int i = n, ans = INF, sum = 0; while (i >= 1) { int l, r = i; while (i > 1 && leg[i].l == leg[i-1].l) { c[leg[i].d]--; i--; } c[leg[i].d]--; l = i; int num = r - l + 1; if (num * 2 -1 >= r) ans = min (ans, sum); else if (num == 1) ans = min (ans, tot - leg[i].d); else { int res = r - (num * 2 - 1); int cost = 0; for (int j=0; j<=200 && res > 0; ++j) { if (c[j]) { int t = min (res, c[j]); res -= t; cost += t * j; } } ans = min (ans, sum + cost); } i--; for (int j=l; j<=r; ++j) sum += leg[j].d; } printf ("%d ", ans); return 0; }
UPD:
字典树+区间DP/Hash+DFS E - Ann and Half-Palindrome
题意:一个长度n的字符串(n<=5000),仅含有a和b,定义半回文串:满足当i是奇数位置,且str[i]=str[n-1-i](i<=(n+1)/2)的串,求所有子串按照字典序后第k个字串。
思路:没注意到复杂度是可以接受的。首先区间DP判断子串[i,j]是否为半回文串,然后将所有子串插入字典树上,插入的过程中标记是半回文串的点,然后DFS中序遍历,找第k个。
#include <bits/stdc++.h> const int N = 5e3 + 5; const int NODE = N * (N+1) / 2; int ch[NODE][2]; int val[NODE]; int sz; char str[N]; bool dp[N][N]; int n, k; void trie_init() { //memset (ch[0], 0, sizeof (ch[0])); //val[0] = 0; sz = 1; } void trie_insert(char *s, int start) { int u = 0, c; for (int i=start; i<n; ++i) { c = s[i]-'a'; if (!ch[u][c]) { memset (ch[sz], 0, sizeof (ch[sz])); val[sz] = 0; ch[u][c] = sz++; } u = ch[u][c]; if (dp[start][i]) val[u]++; } } char path[N]; int m; bool trie_DFS(int u) { k -= val[u]; if (k <= 0) return true; //printf, exit (0); for (int i=0; i<2; ++i) { if (ch[u][i]) { path[m++] = 'a' + i; path[m] = ' '; if (trie_DFS (ch[u][i])) return true; path[--m] = ' '; } } return false; } void solve(char *s) { n = strlen (s); for (int i=0; i<n; ++i) { for (int j=i; j>=0; --j) { if (i - j <= 3) { dp[j][i] = (s[j] == s[i]); } else { dp[j][i] = (s[j] == s[i]) && dp[j+2][i-2]; } } } trie_init (); for (int i=0; i<n; ++i) { trie_insert (s, i); } m = 0; trie_DFS (0); printf ("%s ", path); } int main() { scanf ("%s%d", str, &k); solve (str); return 0; }
再附上铭神的hash+指针字典树代码
#include <bits/stdc++.h> using ull = unsigned long long; const int N = 5e3 + 5; const ull base = 233; ull base_pow[N]; ull lhash[N], rhash[N]; char str[N]; int n, k; ull get_hash(ull *hash, int p, int len) { if (len & 1) len++; return hash[p] - hash[p+len] * base_pow[len/2]; } struct Node { Node *go[2]; int cnt; }; Node pool[N*N], *alloc, *root; void insert(int p) { Node *u = root; for (int i=0; p+i<n; ++i) { int c = str[p+i] - 'a'; if (u->go[c] == NULL) { u->go[c] = alloc++; } u = u->go[c]; int len = i + 1; if (get_hash (lhash, p, len+1>>1) == get_hash (rhash, n-1-(p+i), len+1>>1)) { u->cnt++; } } } std::vector<int> path; bool DFS(Node *u) { if (u == NULL) return false; k -= u->cnt; if (k <= 0) return true; path.push_back(0); if (DFS(u->go[0])) return true; path.back() = 1; if (DFS(u->go[1])) return true; path.pop_back(); return false; } int main() { scanf ("%s%d", str, &k); n = strlen (str); base_pow[0] = 1; for (int i=1; i<N; ++i) { base_pow[i] = base_pow[i-1] * base; } for (int i=n-1; i>=0; --i) { lhash[i] = lhash[i+2] * base + str[i]; rhash[i] = rhash[i+2] * base + str[n-1-i]; } alloc = pool; root = alloc++; for (int i=0; i<n; ++i) { insert (i); } DFS (root); for (auto t: path) { putchar ('a' + t); } puts (""); return 0; }
未完待续~