前言
本来准备看日报很久了,但是一直没有看懂,今天才恍然大悟。特此,记之。
拉格朗日乘数法
对于一个多元函数 (F(x,y,z,..)),我们假如我们需要满足 (varphi(x,y,z,...)=0),那么我们想要求出这个函数的最值,我们就可以使用拉格朗日乘数法,具体来说,我们可以我们就是要让:
是个极值。举个例子,如果我们要让这个玩意最小,那么,在 (lambda) 为某种取值的时候,我们肯定会使 (varphi(x,y,z,...)) 为 (0)。最大值相似。
考虑这个玩意怎么求极值,可以发现其实就是在求导之后为 (0) 的点就是极值,整体求导说白了就是每个变量求偏导,你只需要让这个东西为 (0) 即可。
一些题目
[NOI2012]骑行川藏
思路
不难看出题目就是这样一个意思:
让
最小。
不难看出就是:
直接套版子就可以得到:
可以直接二分 (lambda) ,然后在内部二分 (v_i) 判断即可。
时间复杂度 (Theta(nlog^2w)),其中 (w) 是值域。
( exttt{Code})
#include <bits/stdc++.h>
using namespace std;
#define double long double
#define Int register int
#define MAXN 10005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
int n;
double Eu,k[MAXN],s[MAXN],v[MAXN],v1[MAXN];
bool check (double lamb){
double res = 0;
for (Int i = 1;i <= n;++ i){
double l = max ((double)0,v1[i]),r = 1e5;
while (r - l > 1e-12){
double mid = (l + r) * 0.5;
if (2 * lamb * k[i] * mid * mid * (mid - v1[i]) <= 1) l = mid;
else r = mid;
}
v[i] = r,res += k[i] * (r - v1[i]) * (r - v1[i]) * s[i];
}
return res <= Eu;
}
signed main(){
read (n),scanf ("%Lf",&Eu);
for (Int i = 1;i <= n;++ i) scanf ("%Lf%Lf%Lf",&s[i],&k[i],&v1[i]);
double l = 0,r = 1e5;
while (r - l > 1e-12){
double mid = (l + r) * 0.5;
if (check (mid)) r = mid;
else l = mid;
}
double res = 0;
for (Int i = 1;i <= n;++ i) res += s[i] * 1.0 / v[i];
printf ("%.9Lf
",res);
return 0;
}
CF1344D Résumé Review
思路
跟上面一个类似,不过不是那么板了。
不难发现我们可以按 (a_i) 排序,然后对于一个 (lambda),有 (b_i=sqrt{(a_i-lambda)/3})。
但是因为 (b_ile a_i) 的限制,而且 (sum_{i=1}^{n} b_i=k),所以我们并不能这样搞。但是我们排序后,我们不满足条件 (b_i> a_i) 的一定是前面一段(因为是个凹函数),所以我们可以二分一下前面多少个为 (a_i)。然后我们只需要判断 (sum b_i) 是否 (ge k)。
但是并没有完,我们显然 (b_i) 要下取整,而且我们还并不能完全保证 (sum b_i=k),所以我们可以把一些 (b_i) 增大,可以保证的是,一个数最多会增大 (1),至于选哪些直接贪心看选哪些贡献最大就好了。
时间复杂度 (Theta(nlog^2 w))。
( exttt{Code})
#include <bits/stdc++.h>
using namespace std;
#define double long double
#define Int register int
#define int long long
#define MAXN 100005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
double b[MAXN];
int n,k,a[MAXN],ansf[MAXN],index[MAXN];
struct node{
int ind,v;
bool operator < (const node &p)const{return v < p.v;}
}c[MAXN];
bool check (int x){
double l = -1e20,r = 1e20,tot = 0,mid;int k1 = k;bool flg = 0;
for (Int i = 1;i <= x;++ i) k1 -= a[i];
for (Int i = x + 1;i <= n;++ i) l = max (l,(double)a[i] - 3 * a[i] * a[i]),r = min (r,(double)a[i]);
while (r - l > 1){
mid = (l + r) * 0.5,tot = 0;
for (Int i = x + 1;i <= n;++ i) b[i] = sqrt ((a[i] - mid) / 3),tot += b[i];
if (tot >= k1) l = mid,flg = 1;
else r = mid;
}
for (Int i = x + 1;i <= n;++ i) b[i] = sqrt ((a[i] - l) / 3);
return flg;
}
signed main(){
read (n,k);
for (Int i = 1;i <= n;++ i) read (c[i].v),c[i].ind = i;
sort (c + 1,c + n + 1);for (Int i = 1;i <= n;++ i) a[i] = c[i].v,index[i] = c[i].ind;
int l = 0,r = n,ans = 0;
while (l <= r){
int mid = (l + r) >> 1;
if (check (mid)) r = mid - 1,ans = mid;
else l = mid + 1;
}
for (Int i = 1;i <= ans;++ i) b[i] = a[i];check (ans);
for (Int i = 1;i <= n;++ i) b[i] = floor (b[i]),k -= b[i];
int tot = 0;for (Int i = 1;i <= n;++ i) if (b[i] < a[i]) c[++ tot] = node {i,a[i] - 3 * b[i] - 3 * b[i] * b[i]};
sort (c + 1,c + tot + 1),reverse (c + 1,c + tot + 1);
for (Int i = 1;i <= tot && i <= k;++ i) b[c[i].ind] ++;
for (Int i = 1;i <= n;++ i) ansf[index[i]] = b[i];
for (Int i = 1;i <= n;++ i) write (ansf[i]),putchar (' ');
putchar ('
');
return 0;
}