题目描述:有\(n\)个数,将这\(n\)个数分红\(3\)堆,使得\(3\)堆中和的最大值减最小值最小,求方案。c++
solution
\(f[i][j]\)表示第二堆减第一堆等于\(i\),第三堆减第二堆等于\(j\)的方案。因为数字比较小,因此能够定\(-100 \leq i, j \leq 100\),而后将读入数据随机排序,作\(10\)次就能够过了。socket
时间复杂度:\(O(能过)\)spa
题目描述:有\(n\)个水杯,每一个水杯的容量为\(a_i\),如今有\(3\)种操做:rest
solution
显然,\(m\)必定要是\(a_i\)的最大公约数的倍数,而且\(m\)要小于\(a_i\)的最大值。
而后将最大的水杯看作剩余系,全部水杯往最大的水杯倒水,至关于水量模最大水杯容量,而后像彻底背包那样作便可。code
时间复杂度:\(O(最大容量*n)\)server
solution排序
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=30; LL A, B; int a[maxn], b[maxn], now[maxn]; void calc(LL num, int *a) { if (num==0) a[a[0]=1]=0; a[0]=0; while (num) { a[++a[0]]=num%10; num/=10; } } bool check() { if (now[0]!=a[0]) return now[0]>a[0]; for (int i=now[0]; i; --i) if (now[i]!=a[i]) return now[i]>a[i]; return true; } void solve() { scanf("%lld%lld", &A, &B); calc(A, a); calc(B, b); LL answer=1, ans=B; for (int i=1; i<=b[0]; ++i) answer*=b[i]; for (int i=b[0]; i; --i) { for (int j=b[0]; j>i; --j) now[j]=b[j]; now[i]=b[i]-1; for (int j=i-1; j; --j) now[j]=9; for (int j=i; j<b[0]; ++j) if (now[j]<0) { now[j]+=10; now[j+1]--; } else break; now[0]=b[0]; if (now[0]>1 && now[now[0]]==0) --now[0]; if (check()) { LL s=1; for (int j=1; j<=now[0]; ++j) s*=now[j]; if (s>answer) { answer=s; ans=0; for (int j=now[0]; j; --j) ans=ans*10+now[j]; } } } printf("%lld\n", ans); } int main() { solve(); return 0; }
题目描述:分别给定\(n, m\)个向量,对于\(n\)里面的每一个向量,在\(m\)中找一个向量,使得两个向量的点乘最小。three
solution
根据点乘的意义,至关因而一个向量在另外一个向量的投影,所以能够想到只有\(m\)个向量造成的下凸壳,在下凸壳上的点才是答案,将\(n\)个向量极角排序,而后每一个向量对应的答案在下凸壳上是单调的。it
时间复杂度:\(O(nlogn)\)io
题目描述:有一个未知序列\(a_i\),给出一些数据,第\(i\)行第\(j\)个数据表示某个数在序列的第\(i+1\)个数后面(包括\(i+1\))第一次出现的位置是\(j\),找出\(a_i\)的可行解中字典序最小的一个。
solution
题目还能够用另外一种方式表达:第\(i\)行第\(j\)个数据表示某个数在序列的第\(i+1\)位到第\(j-1\)位没出现过,进而能够求出\(a_i\)在\([l_i, i-1]\)没出现过,而后只要贪心地从左到右放数,每次取\([1, l_i-1]\)中最小的数,若是\(l_i=1\),那么新开一个数字给\(a_i\)。
时间复杂度:\(O(nlogn)\)
题目描述:有一个电源(只有一个插座),\(n\)个排插,分别有\(a_i\)个插座,每一个排插能够直接接电源或者插到别的排插上,有\(m\)部手机须要同时充电,每部手机规定到电源之间最多能有\(b_i\)个排插,问最多能有多少部手机同时充电。
solution
将排插按\(a_i\)从大到小排序,手机按\(b_i\)从小到大排序,二分答案,先知足\(b_i\)较小的,其它位置插排插。
时间复杂度:\(O(nlogn)\)
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=int(2e5)+100; int n, m; int a[maxn], b[maxn]; void read() { scanf("%d%d", &n, &m); for (int i=1; i<=n; ++i) scanf("%d", &a[i]); for (int i=1; i<=m; ++i) scanf("%d", &b[i]); } bool check(int num) { LL rest=1; for (int i=1, level=0; i<=n && num; ++level) { int k=num; while (k && b[k]==level) --k; if (num-k>rest) return false; LL tmp=(rest-=(num-k)); num=k; while (i<=n && (tmp--)) rest+=a[i++]-1; } return rest>=num; } void solve() { sort(a+1, a+1+n, greater<int>()); sort(b+1, b+1+m, greater<int>()); int L=0, R=m+1; while (L+1<R) { int mid=(L+R)>>1; if (check(mid)) L=mid; else R=mid; } printf("%d\n", L); } int main() { read(); solve(); return 0; }
题目描述:给定一棵树,边权为\(1\),如今能够选择一条长度不超过\(m\)的链,将链上的边权全改成\(0\),使得树的直径最小,求方案。
solution
思路挺好想的,就是枚举一端做为树根,而后相似树形\(dp\)算出当另外一端某个点时,树的直径是多少。
时间复杂度:\(O(n^2)\)
#include <bits/stdc++.h> using namespace std; const int maxn=5010; int n, m, root; vector<int> out[maxn]; int idx[maxn]; pair<int, int> le[maxn], ri[maxn]; int radle[maxn], radri[maxn]; int fa[maxn], f[maxn], g[maxn], h[maxn]; int rad[maxn], deep[maxn]; pair<int, int> ans, solu; void read() { scanf("%d%d", &n, &m); for (int i=0; i<n; ++i) out[i].clear(); for (int i=1; i<n; ++i) { int u, v; scanf("%d%d", &u, &v); out[u].push_back(v); out[v].push_back(u); } } void dfs1(int cur, int _fa) { rad[cur]=0; f[cur]=0; fa[cur]=_fa; if (_fa==-1) deep[cur]=0; else deep[cur]=deep[_fa]+1; for (auto &to:out[cur]) if (to!=_fa) { dfs1(to, cur); rad[cur]=max(rad[cur], max(rad[to], f[cur]+f[to]+1)); f[cur]=max(f[cur], f[to]+1); } } void dfs2(int cur, int past, int chain) { if (deep[cur]>m) return; int s=max(past, max(rad[cur], chain+f[cur])); if (make_pair(s, deep[cur])<ans) { ans=make_pair(s, deep[cur]); solu=make_pair(root, cur); } int cnt=0; for (auto &to:out[cur]) if (to!=fa[cur]) idx[++cnt]=to; le[1]=make_pair(0, 0); radle[1]=0; for (int i=2; i<=cnt; ++i) { le[i]=le[i-1]; radle[i]=max(radle[i-1], rad[idx[i-1]]); if (f[idx[i-1]]+1>le[i].first) { le[i].second=le[i].first; le[i].first=f[idx[i-1]]+1; } else le[i].second=max(le[i].second, f[idx[i-1]]+1); } ri[cnt]=make_pair(0, 0); radri[cnt]=0; for (int i=cnt-1; i>0; --i) { ri[i]=ri[i+1]; radri[i]=max(radri[i+1], rad[idx[i+1]]); if (f[idx[i+1]]+1>ri[i].first) { ri[i].second=ri[i].first; ri[i].first=f[idx[i+1]]+1; } else ri[i].second=max(ri[i].second, f[idx[i+1]]+1); } for (int i=1; i<=cnt; ++i) { g[idx[i]]=max(le[i].first, ri[i].first); h[idx[i]]=max(le[i].first+ri[i].first, max(le[i].first+le[i].second, ri[i].first+ri[i].second)); h[idx[i]]=max(h[idx[i]], max(radle[i], radri[i])); } for (auto &to:out[cur]) if (to!=fa[cur]) dfs2(to, max(past, max(h[to], chain+g[to])), max(chain, g[to])); } void solve() { ans=make_pair(n*2, 0); for (int i=0; i<n; ++i) { root=i; dfs1(i, -1); dfs2(i, 0, 0); } printf("%d\n%d\n", ans.first, ans.second); if (ans.second) printf("%d %d\n", solu.first, solu.second); } int main() { read(); solve(); return 0; }