在电脑中有一种比内存读入还要快的东西叫作缓存,其定义是对于从内存里读入一个数组时,计算机会预测剩余的连续的部分数组是否会被用到,因此将他存进缓存,这样能够直接按顺序读入,不用查询,更加快。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; }