Live2D

拉格朗日乘数法 学习小记

前言

本来准备看日报很久了,但是一直没有看懂,今天才恍然大悟。特此,记之。

拉格朗日乘数法

对于一个多元函数 \(F(x,y,z,..)\),我们假如我们需要满足 \(\varphi(x,y,z,...)=0\),那么我们想要求出这个函数的最值,我们就可以使用拉格朗日乘数法,具体来说,我们可以我们就是要让:

\[F(x,y,z,...)+\lambda \varphi(x,y,z,...) \]

是个极值。举个例子,如果我们要让这个玩意最小,那么,在 \(\lambda\) 为某种取值的时候,我们肯定会使 \(\varphi(x,y,z,...)\)\(0\)。最大值相似。

考虑这个玩意怎么求极值,可以发现其实就是在求导之后为 \(0\) 的点就是极值,整体求导说白了就是每个变量求偏导,你只需要让这个东西为 \(0\) 即可。

一些题目

[NOI2012]骑行川藏

题目传送门

思路

不难看出题目就是这样一个意思:

\[\sum_{i=1}^{n} k_i(v_i-v_i^{'})^2s_i=E_u \]

\[\sum_{i=1}^{n} \frac{s_i}{v_i} \]

最小。

不难看出就是:

\[F(v_1,v_2,...,v_n)=\sum_{i=1}^{n} \frac{s_i}{v_i},\varphi(v_1,v_2,...,v_n)=\sum_{i=1}^{n} k_i(v_i-v_i^{'})^2s_i-E_u \]

直接套版子就可以得到:

\[\left\{\begin{array}{l} 2\lambda k_iv_i^2(v_i-v_i^{'})=1\\ \sum_{i=1}^{n} k_i(v_i-v_i^{'})^2s_i=E_u \end{array}\right.\]

可以直接二分 \(\lambda\) ,然后在内部二分 \(v_i\) 判断即可。

时间复杂度 \(\Theta(n\log^2w)\),其中 \(w\) 是值域。

\(\texttt{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\n",res);
	return 0;
}

CF1344D Résumé Review

题目传送门

思路

跟上面一个类似,不过不是那么板了。

不难发现我们可以按 \(a_i\) 排序,然后对于一个 \(\lambda\),有 \(b_i=\sqrt{(a_i-\lambda)/3}\)

但是因为 \(b_i\le 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(n\log^2 w)\)

\(\texttt{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 ('\n');
	return 0;
}
posted @ 2020-11-26 22:21  Dark_Romance  阅读(212)  评论(0编辑  收藏  举报