题意:给定股票每天的价格a[]
,要求计算可以获得的最大利润(交易次数不限,只能买一股)
思考:贪心。若最优解有在第i
天买,第j
天卖,那么利润等同于连续在i...j
天买卖。所以只要a[i-1]<a[i]
,就把它算到利润中去。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> #include <algorithm> using namespace std;typedef long long LL;int n,a[100010 ];LL ans; int main () { cin>>n; for (int i=1 ;i<=n;i++){ scanf ("%d" ,&a[i]); if (i!=1 &&a[i]>a[i-1 ]) ans+=a[i]-a[i-1 ]; } cout<<ans; return 0 ; }
技巧总结:将最优解的一部分等价替换为一系列简单的操作。
题意:线段上N家商店,位置已给定,要求选取一个仓库,到所有商店的距离和最小
思考:其实就是小学奥数题。先猜将仓库建到中间,证明:推表达式,形式是一系列绝对值的和,将他们按照在仓库左边/在仓库右边分组,可以去掉绝对值。最后发现取中间时最小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <algorithm> using namespace std;const int N = 100005 ;int n, res;int a[N];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 ++ ) res += abs (a[i] - a[n >> 1 ]); printf ("%d\n" , res); return 0 ; }
技巧总结:数据范围1e5,大概率nlong n,不妨排个序。
题意:有 n个小朋友坐成一圈,每人有 a[i]
个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为 1。求使所有人获得均等糖果的最小代价。
思考:高中数学题。看数据范围,不太可能用dp,先试着用推导数学公式。
假设标号为i的小朋友开始有Ai颗糖果,Xi表示第i个小朋友给了第i-1个小朋友Xi颗糖果,如果Xi<0,说明第i-1个小朋友给了第i个小朋友Xi颗糖果,X1表示第一个小朋友给第n个小朋友的糖果数量。 所以最后的答案就是ans=|X1| + |X2| + |X3| + ……+ |Xn|。
按定义。有A1-X1+X2=ave。A2-X2+X3=ave……
最终,我们可以得到n个方程,一共有n个变量,但是因为从前n-1个方程可以推导出最后一个方程,所以实际上只有n-1个方程是有用的。尽管无法直接解出答案,但可以用X1表示出其他的Xi,那么本题就变成了单变量的极值问题。
对于第1个小朋友,A1-X1+X2=ave -> X2=ave-A1+X1 = X1-C1(假设C1=A1-ave,下面类似) 对于第2个小朋友,A2-X2+X3=ave -> X3=ave-A2+X2=2ave-A1-A2+X1=X1-C2 即C2=A1+A2-2ave=A2+C1-ave以此类推Ci的通式为Ci=Ai+C[i-1]-ave
我们希望Xi的绝对值之和尽量小,即|X1| + |X1-C1| + |X1-C2| + ……+ |X1-Cn-1|要尽量小。注意到|X1-Ci|的几何意义是数轴上的点X1到Ci的距离,所以这道题变成了上面货仓选址
这题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iostream> #include <algorithm> #define ll long long using namespace std;int n,a[1000001 ],c[1000001 ],ave;ll sum;int main () { scanf ("%d" ,&n); for (int i=1 ;i<=n;i++) { scanf ("%d" ,&a[i]); sum+=a[i]; } ave=sum/n; for (int i=2 ;i<=n;i++) c[i]=c[i-1 ]+a[i]-ave; sort (c+1 ,c+n+1 ); ll ans=0 ; int mid=c[(n>>1 )+1 ]; for (int i=1 ;i<=n;i++) ans+=abs (c[i]-mid); printf ("%lld" ,ans); return 0 ; }
技巧总结:遇到这种题就要耐心推数学公式
题意:一个平面直角坐标系,雷达位于x轴上,监测范围是半径为d的圆,有若干小岛位于x轴上方,问需要多少雷达能监测所有小岛。
思考:计算雷达监测的范围较困难。我们计算监测某个小岛,雷达需要在的范围。对于每个小岛,都计算出这样一个区间。那么就转换成了经典的贪心问题:区间选点 。先对右端点排个序,然后从左到右依次选未覆盖区间的右端点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <cstring> #include <iostream> #include <algorithm> #include <cmath> using namespace std;typedef pair<double , double > PDD;const int N = 1010 ;const double eps = 1e-6 , INF = 1e10 ;int n, d;PDD seg[N]; int main () { cin >> n >> d; bool success = true ; for (int i = 0 ; i < n; i ++ ) { int x, y; cin >> x >> y; if (y > d) { success = false ; break ; } auto len = sqrt (d * d - y * y); seg[i] = {x + len, x - len}; } if (!success) puts ("-1" ); else { sort (seg, seg + n); int res = 0 ; double last = -INF; for (int i = 0 ; i < n; i ++ ) { if (seg[i].second > last + eps) { res ++ ; last = seg[i].first; } } cout << res << endl; } return 0 ; }
技巧总结:逆向思考,区间选点贪心。
题意:给定 N
个闭区间 [ai,bi][ai,bi]
,请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。输出可选取区间的最大数量。
思考:最大不相交区间数==最少覆盖区间点数。因为如果几个区间能被同一个点覆盖,说明他们相交了,所以有几个点就是有几个不相交区间