[浅谈] 邻项交换排序贪心
\(\color{darkblue}\text{参考文献}\)
\(\color{darkblue}\text{总结}\)
对于一些决定贪心顺序的题目,我们可以通过确定相邻两个人的排列关系来确定最优顺序。但是相邻最优推广到全局最优还需要证明两种关系:
-
可比传递性。 当 \(a_i<a_j\) , \(a_j<a_k\) 时,还应当保证 \(a_i<a_k\) 。
-
不可比传递性,如果 \(a_i=a_j\) , \(a_j=a_k\) ,还应当保证 \(a_i<=a_k\) 。
\(\color{darkblue}\text{例题}\)
\(\color{purple}\text{P1080 [NOIP2012 提高组] 国王游戏}\)
考虑相邻的人 \(P_1\) 和 \(P_2\) ,设他们左右手分别为 \(a_i\) , \(b_i\) ,设他们前面的人左手乘积为 \(t\) 。
如果 \(P_1\) 在前, \(ans=\max(\frac{t}{b_1},\frac{t\cdot a_1}{b_2})\)
\(P_2\) 在前同理, \(ans=\max(\frac{t}{b_2},\frac{t\cdot a_2}{b_1})\)
其实已经可以做了。但是这个式子仍能化简。\(\frac{t\cdot a_2}{b_1}>\frac{t}{b_1}\) , \(\frac{t\cdot a_1}{b_2}>\frac{t}{b_2}\) ,所以可以变成比较 \(\frac{t\cdot a_2}{b_1}\) 与 \(\frac{t\cdot a_1}{b_2}\) 。
最后,我讨厌高精度。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5010;
int read(){
int x=0,f=1;char c=getchar();
while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
struct node{
int a,b;
}e[N];
bool cmp(node t1,node t2){
return t2.a*t2.b>t1.a*t1.b;
}
int n,A[N],B[N];
struct NUM{
int len,num[10000010];
}S1,S2,S3;
void add(int x){
for(int i=1;i<=S1.len;i++)S1.num[i]*=x;
for(int i=1;i<S1.len;i++){
S1.num[i+1]+=S1.num[i]/10;
S1.num[i]%=10;
}
while(S1.num[S1.len]>=10){
S1.num[S1.len+1]+=S1.num[S1.len]/10;
S1.num[S1.len]%=10;
S1.len++;
}
return;
}
void del(int x){
int tmp=0;
S2.len=0;
for(int i=S1.len;i>=1;i--){
tmp=tmp*10+S1.num[i];
if(tmp>=x){
S2.len=max(S2.len,i);
S2.num[i]=tmp/x;
tmp%=x;
}
else S2.num[i]=0;
}
return;
}
bool gt(){
if(S2.len!=S3.len)return S2.len>S3.len;
for(int i=S2.len;i>=1;i--)
if(S2.num[i]!=S3.num[i])
return S2.num[i]>S3.num[i];
}
void rp(){
S3.len=S2.len;
for(int i=S3.len;i>=1;i--)
S3.num[i]=S2.num[i];
return;
}
signed main(){
n=read();
e[0].a=read(),e[0].b=read();
for(int i=1;i<=n;i++)e[i].a=read(),e[i].b=read();
sort(e+1,e+n+1,cmp);
S1.len=1;S1.num[1]=1;
for(int i=1;i<=n;i++){
add(e[i-1].a);
del(e[i].b);
if(gt())rp();
}
for(int i=S3.len;i>=1;i--)
printf("%lld",S3.num[i]);
printf("\n");
S1.len=1;S1.num[1]=1;
return 0;
}
\(\color{purple}\text{P1248 加工生产调度}\)
本做法仅供参考,正确性无法保证。
那两个物品,编号为 \(1\) 和 \(2\) 。
\(1\) 在前: \(ans=a_1+\max(a_2,b_1)+b_2\)
\(2\) 在前: \(ans=a_2+\max(a_1,b_2)+b_1\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int read(){
int x=0,f=1;char c=getchar();
while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
struct node{
int a,b,id;
}e[N];
bool cmp(node t1,node t2){
return min(t1.a,t2.b)<min(t2.a,t1.b);
}
int n,A[N],B[N];
int main(){
n=read();
for(int i=1;i<=n;i++)e[i].a=read();
for(int i=1;i<=n;i++)e[i].b=read(),e[i].id=i;
sort(e+1,e+n+1,cmp);
for(int i=1;i<=n;i++){
A[i]=A[i-1]+e[i].a;
B[i]=max(B[i-1],A[i])+e[i].b;
}
printf("%d\n",B[n]);
for(int i=1;i<=n;i++)
printf("%d ",e[i].id);
printf("\n");
return 0;
}

浙公网安备 33010602011771号