这儿总结了几道具有相似贪心做法的算法题,他的思路是比较独特的,如果没有做过基本很难想出来
基本思路就是将两个权值求和后排序,贪心一般都会先排序,而排序常用的是按照某个变量值进行排序,或者按照两个变量间的比例来排序,但是这类算法题的思路却是按照两个变量的和值来排序,有种启发式算法的感觉
题目一 耍杂技的牛
刷题链接:
https://www.acwing.com/problem/content/description/127/
思路分析:
首先看到题目要求最大值中的最小值跟二分很像,以为能用二分,但实际上不是二分,是贪心算法 ,排序后直接求就行,证明不难,难在不好想出来
因此按照讲解图片,将所有的牛排序后直接计算风险值即可得到最小的结果
AC代码:
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL N=50005,INF=0x3f3f3f3f3f3f3f3f; //long long范围的最大值
struct Node
{
LL w;
LL v;
}a[N];
LL n,ans=-INF; //dp[i][j]表示前i个物品中选出来重量为j的最大价值
bool cmp(Node a,Node b)
{
return a.w+a.v<b.w+b.v; //注意是要加起来,而不是直接按照w排序
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].w>>a[i].v;
}
sort(a+1,a+n+1,cmp);
int sum=0;
for(int i=1;i<=n;i++)
{
ans=max(ans,sum-a[i].v);
sum+=a[i].w;
}
cout<<ans;
return 0;
}
题目二 国王游戏
刷题链接: https://www.acwing.com/problem/content/description/116/
题目分析:
只需要将所有的大臣按照左右手整数乘积从小到大的关系排序即可
更详细的证明见题解:https://www.acwing.com/solution/content/1062/
这道题还涉及了高精度乘除法,需要注意一下
AC代码:
#include<iostream>
#include<algorithm>
#include<sstream>
#include<string.h>
using namespace std;
const int N=10005;
int n,a,b;
struct Node
{
int z;
int y;
}A[N];
bool cmp(Node a,Node b)
{
return a.z+a.y<b.z+b.y;
}
bool dayu(string a,string b)
{
//长度更长的大
if(a.length()>b.length()) return true;
else if(a.length()==b.length()) return a>b;
else return false;
}
string chu(string a,int b)
{
int lena=a.length();
string ans="";
int achu=0,yushu=0,shang=0;
for(int i=0;i<lena;i++)
{
achu=achu*10+(a[i]-'0');
shang =achu/b;
yushu =achu%b;
achu=yushu;
ans+=(shang+'0');
}
//去掉前导0
int i=0;
for(;i<lena;i++) if(ans[i]!='0') break;
return ans.substr(i);
}
string cheng(string a,int b)
{
if(a=="0"||b==0) return "0";
int lena=a.length();
int now=0;
vector<int>ans;
for(int ai=lena-1;ai>=0;ai--) //当a还没结束
{
now+=(a[ai]-'0')*b;
ans.push_back(now%10);
now/=10; //进位
}
while(now)
{
ans.push_back(now%10);
now/=10;
}
//去掉前导0
string res="";
for(int i=ans.size()-1;i>=0;i--) res+=(ans[i]+'0');
return res;
}
int main()
{
cin>>n;
cin>>A[0].z>>A[0].y;
for(int i=1;i<=n;i++)
{
cin>>A[i].z>>A[i].y;
}
sort(A+1,A+n+1,cmp);
string sum="1",ans="0";
for(int i=0;i<=n;i++)
{
//乘法和除法内部只能是O(n),因此不能将两个因子都看做是字符串,b比较小直接当做整数就行
//并且还要避免一切会使得时间不是O(n)的操作
string tmp=chu(sum,A[i].y); //大整数除法
//cout<<sum<<" "<<tmp<<endl;
if(dayu(tmp,ans)) ans=tmp;
sum=cheng(sum,A[i].z);
}
cout<<ans;
return 0;
}
题目三 2022国赛蓝桥杯B组-J题-搬砖
刷题链接:
https://www.dotcpp.com/oj/problem2700.html
题目分析:
相同的思路进行分析
AC代码:
#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
const int N=1005,M=20005;
struct Node
{
int w;
int v;
}a[N];
int n,m,w,v,dp[N][M],ans=0; //dp[i][j]表示前i个物品中选出来重量为j的最大价值
bool cmp(Node a,Node b)
{
return a.w+a.v<b.w+b.v; //注意是要加起来,而不是直接按照w排序
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].w>>a[i].v; m+=a[i].w;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
dp[i][j]=dp[i-1][j];
if(j>=a[i].w&&(j-a[i].w<=a[i].v)) //加个判断,限制一下前i-1个物品的重量就行了
{
dp[i][j]=max(dp[i][j],dp[i-1][j-a[i].w]+a[i].v);
}
}
}
for(int i=1;i<=m;i++) ans=max(ans,dp[n][i]);
cout<<ans;
return 0;
}
实际以后遇到这种类似的题记住模板就行,因为现场推理很可能就想不出来
这种题还有一种分析方法:
可以这样强制定义让i在上面,j在下面,让wi+sum<vj(满足题意可采纳),wj+sum>vi(不满足题意不能采纳),也就是让i在上面的时候j可以选,让i在下面的时候j不可以选了。转化一下这个公式,可以把sum丢掉,sum是上面所有的重量。然后公式就是wi<vj,vi<wj。两个想加可以得到wi+vi<wj+vj。所以按这个排序就行。