贪心-推公式-特殊排序

这儿总结了几道具有相似贪心做法的算法题,他的思路是比较独特的,如果没有做过基本很难想出来
基本思路就是将两个权值求和后排序,贪心一般都会先排序,而排序常用的是按照某个变量值进行排序,或者按照两个变量间的比例来排序,但是这类算法题的思路却是按照两个变量的和值来排序,有种启发式算法的感觉

题目一 耍杂技的牛

刷题链接:
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。所以按这个排序就行。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值