題目描述:有\(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; }