贪心法: 一往直前,贪心法!





2.2.1:最少花费硬币数量问题

问题叙述: 有1元,5元,10元,50元,100元,500元的硬币各c1,c5,c10,c50,c100枚。现在要用这些硬币来支付A元,最少需要多少枚硬币,假定本题至少存在一种支付方案。

//在已有的硬币金额及数量 支付 指定人民币,获取其最小花费金币数

//思路策略:尽量选大硬币来支付

//  1. 使用coinDenominations保存硬币面额
//  2. 循环递减硬币面额, 每次循环选择 金币面额数量与花费钱数所能花费的金币面额数量的最小值(int payCoinCount=min(coinMap.at(coinDenominations.at(i)),payRMB/coinDenominations.at(i));),并累加于统计金币数minCoinCount,payRMB减去其花费金币面额和数量乘积
int GetMinCoinCountPayFor(map<int,int> &coinMap,int payRMB)
{
    vector<int> coinDenominations;
    for_each(coinMap.begin(),coinMap.end(),[&coinDenominations](const pair<int,int> &coinInfo){ coinDenominations.push_back(coinInfo.first);});
    sort(coinDenominations.begin(),coinDenominations.end());

    int minCoinCount=0;
    for(decltype(coinDenominations.size()) i=coinDenominations.size()-1;i>=0;--i){
    	int payCoinCount=min(coinMap.at(coinDenominations.at(i)),payRMB/coinDenominations.at(i));
    	payRMB-=payCoinCount*coinDenominations.at(i);
    	minCoinCount+=payCoinCount;
    	if(payRMB==0)
			break;
	}
    return minCoinCount;
}

int main()
{
	map<int,int> coinMap{{1,200},
						 {5,10},
						 {10,200},
						 {100,2}};
	int payRMB=241;
	cout<<GetMinCoinCountPayFor(coinMap,payRMB);



}



2.2.2 区间调度问题(一往无前,贪心法)

问题叙述:有n项工作,每项工作分别在si 时间开始,在 ti 时间结束。对于每项工作,你都可以选择参与与否。如果选择了参与,那么自始自终都必须全程参与。 此外,参与工作的时间段不能重叠(即便是开始的瞬间和结束的瞬间也是不允许的)


//分析区间调度问题:在容易想到的策略中再次选择正确的规则
//在可选的工作中(于目前已选工作都不重叠的工作)中,每次都选取开始时间最早的工作

//(1)在可选的工作中,每次都选择结束时间最早的工作
// (2) 在可选的工作中,每次都选取用时最短的工作。
//(3)在可选的工作中,每次都选取与最少可选工作有重叠的工作。


//   |----1---|   (0,2)
//        |----2----|  (1,3)
//                    |----3----| (4,6)
//                      |------4--------|   (5,9)
int GetMaxWorkCount(int n, const vector<int> &s,const vector<int> &t){

	//设置数组添加其  工作,将其工作结束时间作为关键字,开始时间为值存入。
	vector<pair<int,int>> workVec;
	workVec.reserve(s.size());
	for (decltype(s.size()) i = 0; i !=s.size(); ++i) {
         pair<int,int> workPair={t.at(i),s.at(i)};
         workVec.push_back(workPair);
	}
	//根据策略1对数组排序,结束时间早晚排序
	sort(workVec.begin(),workVec.end());
    int maxWorkCount=0;
    int curWorkOverTime=0;
    //如果 选择的工作开始时间大于当前工作的结束时间,即可选择并记录
	for (int j = 0; j !=workVec.size(); ++j) {
		if(workVec.at(j).second>curWorkOverTime)
		{
			++maxWorkCount;
			curWorkOverTime=workVec.at(j).first;
		}
		
	}
	return maxWorkCount;

}



2.2.3 字典序最小问题

问题描述:给定长度为N的字符串S,要构造一个长度为N的字符串T,起始,T为空串,随后反复进行下列任意操作。

  • 从S 的头部删除一个字符,加到T的尾部
  • 从S 的尾部删除一个字符,加到T的尾部

目标是要构造字典序最小的字符串T:


//贪心算法  :字典序最小问题

//从头部删除元素到加到T的尾部

//头部大添加头元素,尾部大添加尾部元素,一样大,获取倒序反转的S,若倒序的反转的大于等于正序,选择正序
void GetMinDictionaryOrderStr(const string &S,string &T){

	int l=0,r=S.size()-1;
	while(l!=r)
	{
		if(S.at(l)>S.at(r)) { T+=S.at(r); --r ;}
		else if(S.at(l) < S.at(r)) { T+=S.at(l); ++l;}
		else{
			string aStr(S.begin()+l,S.begin()+r+1);
			string bStr(aStr.rbegin(),aStr.rend());
			if(aStr>bStr) {
				T += S.at(r);
				--r;
			} else{
				T+=S.at(l);
				++l;
			}
		}
	}
	T+=S.at(l);
}

2.2.4 其他例题 (Saruman‘s Army)

问题描述:直线上 有N个点,点 i 的位置是 Xi 。从这 N 个点中选择若干个,给他们加上标记。对每一个点,其距离为 R 以内的区域里必须有带有标记的点(自己本身带有标记的点,可以认为与其距离为 0 的地方有一个带有标记的点)。在满足这个条件的情况下,希望能为尽可能少的点添加标记。请问至少要有多少点被加上标记?


//解法思路:  *------*----------*----------*----------*-*-*-
 //          |     r     |
 //                  |     r     |
 //          |                   |     
 
 //从上图我们可以看出,从设置的起始点+r的范围内的最后一个点被作为标记点,从标记点递推 r 范围外的第一个点被作为 下一个起始点,我们使用while循环递推这种规则即可
int GetMinLabelPointCount(const vector<int> &posVec,int R)
{
	if(posVec.size()<=1)  return posVec.size();
	auto N=posVec.size();
	decltype(N) i=0;
	int minLabelPointCount=0;
	while(i!=N){
		int startPos=posVec.at(i);
		while(i!=N && posVec.at(i)<=startPos+R)  ++i;
		int labelPos=posVec.at(i-1);
		while(i!=N && posVec.at(i) <= labelPos+R ) ++i;
		++minLabelPointCount;
	}
	return minLabelPointCount;
}


2.2.4 其他例题 (Fence Repair)

问题描述:农夫约翰为了修理栅栏,要将一块很长的木板切割成 N 块。准备切成的木板长度为L1,L2…LN, 未切割前木板的长度恰好未切割后木板长度的总和。 每次切断木板时 ,需要的开销为这块木板的长度。 例如长度为 21 的木板要切成长度为 5 ,8 ,8 的三块木板。长 21 的木板切成长为 13 和 8 的板时,开销为 21 . 再将长度 为 13 的板切成长度为 5 和 8 的板 时,开销是 13,于是开销就是 34,请求出按照木板要求将木板切割完最小的开销是多少。


//思路:   二叉树的子节点 * 其深度  =  总消耗
//           消耗最小的话:使子节点最小的板长为深度最大的子节点,由于木板是一分为二,可以理解为二叉树
//        这样的话就是: 每次筛选最短板和次短板 并合二为一,每次累加其和值 ,直到仅仅剩下最后一根木板即可。


// 15的木板 分成 {3,4,5,1,2}
//思路:
         //                           3
/*第一步       { 1,2 { 暂定} }        /\          合并后 n=4   ans=3
                                     1  2
                                               6
                                            /      \
  第二步     {3,3 {暂定}}                   3      3           合并后 n=3   ans=3+6=9
                                          /   \
                                          1    2   
                                          
  第三步     {4,5,{6}}                                        合并后 n=2  ans=3+6+9=18
                                                 6        9
                                            /      \     /  \
                                            3      3     4    5  
                                          /   \
                                          1    2   
   最后一步   {6,9}          
                                                     15              合并后n=1   ans=3+6+9+15=33  退出循环
                                                  /      \
                                                6          9
                                            /      \     /  \
                                            3      3     4    5  
                                          /   \
                                          1    2   
                                          
*/


      

int GetMinCostVigor(vector<int> &woodVec){
	long  long ans=0;

	int N=woodVec.size();
	//直到计算出木板为1块时为止
	while(N>1){                                                             //选择搜索比全排序更快
		//直到计算出最短的板mii1和次短板mii2
		int mii1=0,mii2=1;
		if(woodVec.at(mii1) > woodVec.at(mii2))  swap(mii1,mii2);
		for (int i = 2; i !=N ; ++i) {
			if(woodVec.at(i) < woodVec.at(mii1)){
				mii2 = mii1;
				mii1 = i;
			} else if(woodVec.at(i) < woodVec.at(mii2))
			{
				mii2=i;
			}

		}
		//将两块板合并
		auto t = woodVec.at(mii1)+woodVec.at(mii2);
		ans+=t;
		if(mii1 == N-1) swap(mii1,mii2);
		woodVec.at(mii1) = t;
		woodVec.at(mii2)=woodVec.at(N-1);
		--N;
	}
	return ans;
}

猜你喜欢

转载自blog.csdn.net/chongzi_daima/article/details/104347380