在電腦中有一種比內存讀入還要快的東西叫作緩存,其定義是對於從內存裏讀入一個數組時,計算機會預測剩餘的連續的部分數組是否會被用到,因此將他存進緩存,這樣能夠直接按順序讀入,不用查詢,更加快。node
那麼對於咱們的二維數組 \(f[i][j]\) 他是以長度爲 \(1\times j\) 的 \(i\) 個線段組成一個線性的長鏈,因此連續的數字之間是 \(f[1][1],f[1][2]\), 而並非 \(f[1][1],f[2][1]\),ios
那麼推廣到咱們的 \(for\) 循環上,若咱們按照外層 \(i\),內層 \(j\) 的順序循環,獲得的數組就是按照計算機存儲順序調用,所以能夠直接從緩存裏讀入 ,可是當咱們反過來,計算機沒法預測咱們的順序,即緩存中的數組是無用的(當前),那麼只能調用內存,時間的差距大約在 \(10\)左右c++
因此說對於循環的枚舉順序關係到時間複雜度是很重要的.尤爲是矩陣乘除,不一樣的運輸順序形成的極值可達 \(4\) 倍,這就是後幾個點被卡的緣由,特別虧。數組
解決問題,有 \(m\) 詢問,數列已經排好序,求出小於 \(x\) 的有多少個緩存
知道最大的 \(a_i<= x\) ,那麼就能夠說答案就是 \(i\)函數
int n,m,a[B]; int main() { n=read(),m=read(); for (int i=1;i<=n;i++) a[i]=read(); sort(a+1,a+1+n); while(m--) { int x=read(); int l=1, r=n, ans=0; while(l<=r) { int mid=(l+r)>>1; if(a[mid]<=x) l=mid+1, ans=mid; else r=mid-1; } printf("%d\n",ans); } return 0; }
給定 \(N\) 根繩子,要求你從中分出 \(K\) 段繩子,使得每段繩子長度同樣而且必須是整數,問這 \(K\) 段繩子最長可以是多少。優化
這個題應該好好地說一下,浪費了我一下午,讓個人對double
和int
有了從新的認識spa
作法的話很簡單,二分長度 看行成的段數是否大於 \(K\),二分答案就行了code
可是這個題須要用的精度,由於在二分的過程當中獲得的數始終是小數,可是咱們定義的是整形,因此會無限死循環blog
再就是在最後的強制類型轉換中,我發現了三種不用的輸出方式,獲得了三種不一樣的分數,過程慘痛
cout<<a;
這樣直接是雙精度輸出,沒什麼變化
printf("%.f",a)
這個可神了,竟然會自動四捨五入,太神奇了,我才知道,答案部分正確
int s=(int)a; cout<<s;
強制類型轉換,這個就能夠將整數部分完整的獲得了,這也是最後的正確答案,
double s[B],sum; const double esp=1e-8; int n,k; main() { // freopen(".in", "r", stdin); // freopen(".out", "w", stdout); cin>>n>>k; double mx=0; for (int i=1;i<=n;i++) { cin>>s[i]; mx=max(mx,s[i]); } double l=0,r=mx; while(l+esp<r) { double mid=(l+r)/2; int num=0; for (int i=1;i<=n;i++) num+=s[i]/mid; if(num>=k) l=mid; else r=mid; } int s=(int)l; cout<<s; return 0; }
怎麼對大臣排序???
絕對按照右手排,貌似不對,md
假設 \(n==2\) 時候的到一種 \(cmp\) 的函數,而後直接排序就能夠了,這就是貪心,信息學是一門不須要證實的許可,不用證實,對於\(\%90\) 的貪心題均可這麼作,\(nb\)
咱們不妨設 \(n=2\) 時,最大獲益金幣的人最小
那第一我的 \(A\) 爲 \(a.r,a.l\), 第二我的 \(B\) 爲 \(b.r,b.l\)
設皇上的左右手分別爲 \(z[0].l, z[0].r\)
當 \(A\) 在 \(B\) 的前邊時
\(A\) 的錢
\(B\) 的錢
答案 \(v1\) 爲 \(max\{\frac{z[0].l}{a.r},\frac{z[0].l\times a.l}{b.r}\}\)
當 \(B\) 在 \(A\) 前面時
同理可得答案 \(v2\) 爲 \(max\{\frac{z[0].l}{b.r},\frac{z[0].l\times b.l}{a.r}\}\)
最終答案就是 \(max\{v1,v2\}\)
那麼當 \(n!=2\) 時呢,假設如今就這兩我的須要咱們討論,咱們不是須要國王到本身前面人左手的數嘛,咱們設這個數爲 \(x\) 那麼式子的就是變成
答案就是 \(ans=max\{v1,v2\}\)
當咱們把分母去掉即,\(v1,v2\) 都乘以\(a.r\times b.r\)
化簡得
咱們發現 \(x\) 能夠消掉,即在這裏答案的大小不受 \(x\) 的影響,那麼咱們的式子就能夠變成
有了式子咱們就能夠利用冒泡排序,時間複雜度爲 \(O(n^2)\)
//冒泡排序 #include <iostream> using namespace std; struct node{ int l,r; }; node z[100010]; int cmo(node a, node b)//a是否放在b的前面 { int v1 = max(z[0].l/a.r, z[0].l*a.l / b.r); int v2 = max(z[0].l/b.r, z[0].l*b.l / a.r); if(v1<v2) return 1; else return 0; } int main() { for (int i=1;i<=n;i++) for (int j=1;j<n;j++) { if(cmp(z[j+1],z[j])) swap(z[j+1], z[j]); } }// O(n^2)
其實就變成了比較這四個數的大小,這樣就能夠進行貪心了
貪心式子是否能夠優化呢?
咱們不難發現 \(b.r<b.l\times b.r\) 和 \(a.r<a.l\times a.r\)
那麼咱們比較這是兩個還有意義嘛,他們兩個本質上就比右側的大,因此剔除就行了,所以式子就化簡成
貪心化簡的本質
- 去分母
- 去除公共項
- 出去無用項,即不可能更好
- 簡化完畢!
struct node{ int l, r; }; node z[B]; int ans=1, n; int cmp(node a, node b) { return a.l*a.r<b.l*b.r; } int main() { scanf("%d",&n); scanf("%d%d",&z[0].l,&z[0].r); for (int i=1;i<=n;i++) scanf("%d%d",&z[i].l,&z[i].r); sort(z+1,z+1+n,cmp); for (int i=0;i<n;i++) ans*=z[i].l; printf("%d",ans/z[n].r); return 0; }
#include <bits/stdc++.h> using namespace std; int now[20010],sum[20010],ans[20010],add[20010]; struct Node { int a; int b; long long a_b; }node[1010]; int read() { int ans=0,flag=1; char ch=getchar(); while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar(); if(ch=='-') flag=-1,ch=getchar(); while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar(); return ans*flag; } void times(int x) { memset(add,0,sizeof(add)); for(int i=1;i<=ans[0];i++) { ans[i]=ans[i]*x; add[i+1]+=ans[i]/10; ans[i]%=10; } for(int i=1;i<=ans[0]+4;i++) { ans[i]+=add[i]; if(ans[i]>=10) { ans[i+1]+=ans[i]/10; ans[i]%=10; } if(ans[i]!=0) { ans[0]=max(ans[0],i); } } return ; } int divition(int x) { memset(add,0,sizeof(add)); int q=0; for(int i=ans[0];i>=1;i--) { q*=10; q+=ans[i]; add[i]=q/x; if(add[0]==0 && add[i]!=0) { add[0]=i; } q%=x; } return 0; } bool compare() { if(sum[0]==add[0]) { for(int i=add[0];i>=1;i--) { if(add[i]>sum[i]) return 1; if(add[i]<sum[i]) return 0; } } if(add[0]>sum[0]) return 1; if(add[0]<sum[0]) return 0; } void cp () { memset(sum,0,sizeof(sum)); for(int i=add[0];i>=0;i--) { sum[i]=add[i]; } return ; } bool cmp(Node a,Node b) { return a.a_b<b.a_b; } int main() { int n=read(); for(int i=0;i<=n;i++) { node[i].a=read(),node[i].b=read(); node[i].a_b=node[i].a*node[i].b; } sort(node+1,node+n+1,cmp); ans[0]=1,ans[1]=1; for(int i=1;i<=n;i++) { times(node[i-1].a); divition(node[i].b); if(compare()) { cp(); } } for(int i=sum[0];i>=1;i--) printf("%d",sum[i]); return 0; }
char s[10000000]; int l=0; void print(int x) { int y=0,z=0;//倒寫 while (x!=0) { y=y*10+x%10; z++; x/=10; } for (int a=1;a<=z;a++) { s[l++]=y%10+'0'; y/=10; } s[l++]='\n'; }
題型: 給 \(N\) 個數選 \(k\) 個數
求和最小---最優解問題
求方案數---解數量問題
給出一種符合條件的方案---可行解問題
BFS
DFS
明顯不可能的解直接扔掉
不可能成爲最有解的剪枝
void dfs(int p, int nowsum, int nowuse)//考慮地p,和爲nowsum,選了nowuse個數 { if (nowuse>k) return;//可行性剪枝 if (nowuse + n-p+1 < k) return;//可行性剪枝 if (nowsum>=ans) return;//最優性剪枝 if(p>n) { ans=min(nowsum,k); return; } dfs(p+1,nowsum,nowuse); dfs(p+1,nowsum+a[p];nowsue+1); } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); dfs(1,0,0); printf("%d",ans); return 0; }
若是排序後再接搜索的話,搜索順序會受到影響,即先搜索可行性優解
能夠選擇可使答案更好或者更快地到答案的題
靶心數
void dfs(int p, int nowsum, int nowuse)//考慮地p,和爲nowsum,選了nowuse個數 { if (nowuse>k) return;//可行性剪枝 if (nowuse + n-p+1 < k) return;//可行性剪枝 if (nowsum>=ans) return;//最優性剪枝 if(p>n) { ans=min(nowsum,k); return; } dfs(p+1,nowsum+a[p];nowsue+1); dfs(p+1,nowsum,nowuse); } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+1+n); dfs(1,0,0); printf("%d",ans); return 0; }