临项交换

知周所众,10月2日是一个学贪心的好日子(不是

写一下学习总结吧
先写一下印象最深的最简单的(其实不然临项交换

临项交换

例题一:排队接水
知周所众,排队接水的正解是把接水时间少的人放在队伍的前面,那么这背后的原理是什么呢
我们可以设第i个人和第j个人接水的时长为\(t_i\)\(t_j\),显然交换这两个人的位置不会对之前的总时间和之后的总时间产生影响,所以我们可以直接对是否交换的优劣进行判断;
如果要让原先的方式优于交换之后,那么显然有:

\[T(之前的时间)+t_i(第i个人接水的时间)+t_i(第j个人等待的时间)+t_j(第j个人接水的时间)<T+t_j+t_j+t_i \]

化简一下就能得到:

\[t_i<t_j \]

于是我们就能写出这类问题的大致代码:

#include<bits/stdc++.h>
using namespace std;
int n;
double ans;
struct ren{//步骤一:因为要捆绑排序,用结构体储存信息
	int t,w;
}a[1005];
bool cmp(ren a,ren b){//步骤二:根据每个题写出不同的排序方式(核心)
	return a.t<b.t;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].t;
		a[i].w=i;
	}
	sort(a+1,a+1+n,cmp);
	for(int i=1;i<=n;i++){//步骤三:对排序完后的数据处理
		cout<<a[i].w<<" ";	
		cout<<endl;
		for(int j=1;j<=i-1;j++){
			ans+=a[j].t;
		}
		ans/=n;
	}
	printf("%.2lf",ans);
	return 0;
}

例题二:万能的奶牛
首先想到的可能是先把吃草多的奶牛转移回去,但这明显是不对的,比如有一只吃草为100的奶牛和10只吃草为99的奶牛,而且那只100的奶牛的距离非常远,那显然先转移吃草多的策略就会错;
这里我们可以注意到对于任意两只奶牛,显然交换它们的位置是和排队接水一样对前后都没有影响的;
所以我们仍然可以比较交换第i头和第j头奶牛转移顺序的优劣:

\[T+2*t_i*d_j<T+2*t_j*d_i \]

用同样的方法写出代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,sum[100005];
struct cow{
	int t,d;
}c[100005];
bool cmp(cow a,cow b){
	return a.t*b.d<a.d*b.t;
}
int main(){
	ll ans=0;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>c[i].t>>c[i].d;
	sort(c+1,c+n+1,cmp);
	for(int i=1;i<=n;i++) sum[i]=sum[i-1]+c[i].d;
	for(int i=1;i<=n;i++) ans+=(sum[n]-sum[i])*2*c[i].t;
	cout<<ans;
	return 0;
}

例题三:皇后游戏
稍难一点点的题,给一下题面:

\[c_{i} = \begin{cases} a_{1}+b_{1} & ,i=1 \\ \displaystyle \max \left \{ c_{i-1},\sum_{j=1}^{i}a_{j} \right \} +b_{i} & ,2\leq i \leq n \end{cases} % ![] \]

计算最大的\(c_i\)的最小值;

首先很容易发现\(c_i\)的值是递增的,所以最大的\(c_i\)就是\(c_n\);所以就是在求\(c_n\)的最小值;
由于\(a_i\)的前缀和和\(b_i\)的值是固定不变的,所以要让\(c_n\)最小,每一个\(c\)都要最小;
这个时候我们就将这个问题转换成了和前面一样的临项交换问题:
对于第i个大臣和第j个大臣,考虑交换他们的优劣:

\(如果不交换i和j,那么c_j就等于:\)

\[max(c_i,A+a_i+a_j)+b_j\ \ (A表示i和j前面所有a的和) \]

\[代入c_i=max(C,A+a_i)+b_i\ \ (C表示c_{i-1})\ 可以得到 \]

\[c_j=max(C+b_i+b_j,A+a_i+b_i+b_j,A+a_i+a_j+b_j)\ (把外面的b_j放进去) \]

\(同理,我们可以写出交换之后的c_i:\)

\[c_i=max(C+b_i+b_j,A+a_j+b_i+b_j,A+a_i+a_j+b_i) \]

\(由于bool\ \ cmp里的条件是不交换的条件,所以判断条件是c_j<c_i\)
\(又因为我们知道max(a,x)<=max(b,x)等价于a<=b,所以可以改变符号然后去掉第一项:\)

\[max(A+a_i+b_i+b_j,A+a_i+a_j+b_j)<=max(A+a_j+b_i+b_j,A+a_i+a_j+b_i) \]

\(然后就可以开心得提出所有相同的项,消去A,得到:\)

\[a_i+b_j+max(a_j,b_i)<=a_j+b_i+max(a_i+b_j) \]

至此我们就得到了这个cmp函数的判断条件,不过知周又所众,sort的cmp判断条件里不能有等于,所以要特判一下:当两式相等时,根据a的大小来决策(别问为什么,问就是面向样例编程);

给出代码(骗你的,这段代码能A五道紫,跟我的五倍经验说去吧):

#include<bits/stdc++.h>
#define int long long
using namespace std;
int T,n,c[20005],sum[20005];
struct ren{
	int a,b;
}d[20005];
bool cmp(ren i,ren j){
	if((i.a+j.b+max(j.a,i.b))==(j.a+i.b+max(i.a,j.b))){
		return i.a<j.a;
	}
	return i.a+j.b+max(j.a,i.b)<j.a+i.b+max(i.a,j.b);	
}
signed main(){
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;i++) cin>>d[i].a>>d[i].b;
		sort(d+1,d+n+1,cmp);
		for(int i=1;i<=n;i++){
			sum[i]=sum[i-1]+d[i].a;
			c[i]=max(c[i-1],sum[i])+d[i].b;
		}
		cout<<c[n]<<endl;
	}
	return 0;
}
posted @ 2025-10-02 21:17  Turkey_VII  阅读(14)  评论(0)    收藏  举报