#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 1e5 + 10; int p[N],ans,n; int main() { scanf("%d", &n); for (int i = 0; i < n; i ++ ) { scanf("%d", &p[i]); } for (int i = 0; i < n-1; i ++ ) { if(p[i+1] > p[i]) ans += p[i+1] - p[i];//贪心策略:只要比前一天价格高,就买入 } cout << ans << endl; return 0; }
中位数有非常优秀的性质,比如说在这道题目中,每一个点到中位数的距离,都是满足全局的最有性,而不是局部最优性。
具体的来说,我们设在仓库左边的所有点,到仓库的距离之和为p,右边的距离之和则为q,那么我们就必须让p+q的值尽量小。
当仓库向左移动的话,p会减少x,但是q会增加n−x,所以说当为仓库中位数的时候,p+q最小。
还是同样的一句话,画图理解很重要。
#include <iostream> #include <cstring> #include <algorithm> using namespace std; typedef long long LL; const int N = 1e5 + 10; int a[N],n; LL ans; int main() { scanf("%d", &n); for (int i = 0; i < n; i ++ ) { scanf("%d", &a[i]); } sort(a,a+n); for (int i = 0; i < n; i ++ ) { ans += abs(a[n>>1] - a[i]); } cout << ans <<endl; return 0; }
#include <iostream> #include <cstring> #include <algorithm> using namespace std; typedef long long LL; const int N = 1e6 + 10; int a[N],c[N]; int n; LL ave,ans; int main() { scanf("%d", &n); for (int i = 1; i <= n; i ++ ) { scanf("%d", &a[i]); ave += a[i]; } ave /= n; for(int i = n;i>1;i--) { c[i] = c[i+1] + ave - a[i]; } c[1] = 0; sort(c+1,c+n+1); for (int i = 1; i <= n; i ++ ) { ans += abs(c[i] - c[(i+1)/2]); } cout << ans << endl; return 0; }
#include <iostream> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int N = 1010; struct Range { double l; double r; bool operator<(struct Range& t) const { return r < t.r; } }range[N]; int n,d,x,y; int main() { scanf("%d%d", &n,&d); for (int i = 0; i < n; i ++ ) { scanf("%d%d", &x, &y); if(y > d) //如果y大于检测半径,则无法覆盖到 { cout << -1 << endl; return 0; } double t = sqrt(d*d - y*y); //安置能够监测到该小岛的雷达的范围,勾股定理 range[i] = {x-t,x+t}; } sort(range,range+n); //区间选点思路做法,右端点排序 int ans = 1; double ed = range[0].r; for (int i = 1; i < n; i ++ ) { double l = range[i].l,r = range[i].r; if(l > ed) //若当前区间左端点比上一个选出的区间的左端点大,说明必须再拿一个点表示该区间 { ++ans; ed = r; } //否则的话就舍弃该区间 } cout << ans << endl; return 0; }
升序排序后,均摊当前剩下的费用,带钱少的有多少付多少
#include <iostream> #include <cstring> #include <algorithm> #include <cmath> using namespace std; typedef long long LL; const int N = 5e5 + 10; int n; double s; int a[N]; double ave,ans; int main() { scanf("%d%lf", &n,&s); for (int i = 0; i < n; i ++ ) { scanf("%d", &a[i]); } sort(a,a+n); //升序排序 ave = 1.0*s/n; for (int i = 0; i < n; i ++ ) { double cur = 1.0*s/(n-i); //均摊当前剩余款项 if(a[i] < cur) cur = a[i]; //钱不够的有多少付多少,钱够的付平均值 ans += (cur - ave) * (cur - ave); s -= cur; } ans = 1.0*ans/n; printf("%.4lf\n",sqrt(ans)); return 0; }
C++中,正数取模正数是正数,负数取模正数是负数,取模的数的正负对结果无关
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 1e5 + 10,MOD = 1000000009; typedef long long LL; int n,k; int a[N]; int main() { scanf("%d%d", &n, &k); for (int i = 0; i < n; i ++ ) { scanf("%d", &a[i]); } sort(a,a+n); LL ans = 1; int l = 0,r = n-1; int sign = 1; //判断整个数组是否全为负数,是为-1,否则为1 if(k%2 == 1) //选奇数个数 { ans = a[r--]; //此情况下,最大值必选 --k; if(ans < 0) sign = -1; //最大值是负数,整个数组全是负数 } //如果选偶数个,成对选择乘积较大的即可 while(k) { LL x = (LL)a[l] * a[l+1],y = (LL)a[r] * a[r-1]; //选取左右两组数 if(x * sign < y * sign) //如果全为负数,就要选绝对值较小的去乘,否则选绝对值较大的去乘 { ans = y % MOD * ans % MOD; //一定要先取余,后相乘 r-=2; } else { ans = x % MOD * ans % MOD; //一定要先取余,后相乘 l+=2; } k-=2; } cout << ans << endl; return 0; }
逆波兰表达式可以通过"加括号"的方式调整运算顺序,即把负号变成正号,尽可能的给每个负数配一个负号,给每个正数配一个正号
至少减一个数,加一个数,贪心策略就是:减最小的,加最大的
一般情况:把所有数排个序,最大的拿出来,放首项,把最小的数拿出来,给他一个减号,再套一个括号,那么现在还未完成的表达式长这样:
可以发现,现在如果我想加一个数的话,给它一个加号,放在括号外面,也可以给它一个减号,放在括号里面;减一个数同理。换句话说,只要用一个减号,一个最大值,一个最小值,其他数我想加就加,想减就减。那么为了使结果最大,我加上正数,减去负数,就是直接加上所有剩下数的绝对值,那么就解决了。
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 1e5 + 10; typedef long long LL; int a[2*N],n,m; //注意数的个数可以为两倍的N int main() { scanf("%d%d", &n, &m); int k = n + m + 1; for (int i = 0; i < k; i ++ ) { scanf("%d", &a[i]); } LL ans = 0; if(m==0) //没有减号,直接全加 { for (int i = 0; i < k; i ++ ) { ans += a[i]; } } else //有减号 { sort(a,a+k); //也可以不排序,直接找最值 ans = a[k-1] - a[0]; // 贪心策略:加上最大值,减去最小值 for (int i = 1; i < k-1; i ++ ) //其余的数想加就加,想减就减,贪心策略:负数就减,正数就加 { ans += abs(a[i]); } } cout << ans << endl; return 0; }
由a[i] = s[i] - s[i-1]
与题意,要让a[i]的绝对值尽可能小,就要让前缀和数组s两两之间的差的绝对值尽可能小
贪心策略:对前缀和数组进行冒泡排序
#include <iostream> #include <cstring> #include <algorithm> using namespace std; typedef long long LL; const int N = 300005; int T; LL a[N],s[N],n; bool st[N]; int main() { scanf("%d", &T); while (T -- ) { scanf("%d", &n); s[0] = 0; for (int i = 1; i <= n; i ++ ) { scanf("%lld", &a[i]); s[i] = s[i-1] + a[i]; } LL s0 = s[0],sn = s[n]; if(s0 > sn) swap(s0,sn); sort(s,s+n+1); for (int i = 0; i < n; i ++ ) { if(s[i] == s0) { s0 = i; break; } } for (int i = n; i >= 0; i -- ) { if(s[i] == sn) { sn = i; break; } } memset(st, 0, sizeof st); int l = 0,r = n; for(int i = s0;i>=0;i-=2) { a[l++] = s[i]; st[i] = true; } for(int i = sn;i<=n;i+=2) { a[r--] = s[i]; st[i] = true; } for (int i = 0; i <= n; i ++ ) { if(!st[i]) { a[l++] = s[i]; } } LL res = 0; for (int i = 1; i <= n; i ++ ) res = max(res,abs(a[i] - a[i-1])); cout<<res<<endl; } return 0; }