贪心算法
Posted 橘生淮南终洛枳
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了贪心算法相关的知识,希望对你有一定的参考价值。
1.洛谷 P1650 赛马
2.codevs 2181 田忌赛马
题目描述
我国历史上有个著名的故事: 那是在2300年以前。齐国的大将军田忌喜欢赛马。他经常和齐王赛马。他和齐王都有三匹马:常规马,上级马,超级马。一共赛三局,每局的胜者可以从负者这里取得200银币。每匹马只能用一次。齐王的马好,同等级的马,齐王的总是比田忌的要好一点。于是每次和齐王赛马,田忌总会输600银币。
田忌很沮丧,直到他遇到了著名的军师――孙膑。田忌采用了孙膑的计策之后,三场比赛下来,轻松而优雅地赢了齐王200银币。这实在是个很简单的计策。由于齐王总是先出最好的马,再出次好的,所以田忌用常规马对齐王的超级马,用自己的超级马对齐王的上级马,用自己的上级马对齐王的常规马,以两胜一负的战绩赢得200银币。实在很简单。
如果不止三匹马怎么办?这个问题很显然可以转化成一个二分图最佳匹配的问题。把田忌的马放左边,把齐王的马放右边。田忌的马A和齐王的B之间,如果田忌的马胜,则连一条权为200的边;如果平局,则连一条权为0的边;如果输,则连一条权为-200的边……如果你不会求最佳匹配,用最小费用最大流也可以啊。 然而,赛马问题是一种特殊的二分图最佳匹配的问题,上面的算法过于先进了,简直是杀鸡用牛刀。现在,就请你设计一个简单的算法解决这个问题。
输入输出格式
输入格式:
第一行一个整数n,表示他们各有几匹马(两人拥有的马的数目相同)。第二行n个整数,每个整数都代表田忌的某匹马的速度值(0 <= 速度值<= 100)。第三行n个整数,描述齐王的马的速度值。两马相遇,根据速度值的大小就可以知道哪匹马会胜出。如果速度值相同,则和局,谁也不拿钱。
【数据规模】
对于20%的数据,1<=N<=65;
对于40%的数据,1<=N<=250;
对于100%的数据,1<=N<=2000。
输出格式:
仅一行,一个整数,表示田忌最大能得到多少银币。
输入输出样例
3 92 83 71 95 87 74
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 6 int a[2333],b[2333],n,ans; 7 8 int main() { 9 cin>>n; 10 for(int i=1; i<=n; i++) cin>>a[i]; 11 for(int i=1; i<=n; i++) cin>>b[i]; 12 sort(a+1,a+n+1); 13 sort(b+1,b+n+1); 14 int tj_b=1,tj_e=n,qw_b=1,qw_e=n; 15 while(tj_b<=tj_e) { 16 if(a[tj_b]>b[qw_b]) { 17 ans+=200,tj_b++,qw_b++; 18 } 19 else 20 if(a[tj_e]>b[qw_e]) { 21 ans+=200,tj_e--,qw_e--; 22 } 23 else{ 24 if(a[tj_b]<b[qw_e]) ans-=200; 25 tj_b++,qw_e--; 26 } 27 } 28 if(ans>0) cout<<ans; 29 else cout<<"0"; 30 return 0; 31 }
3.洛谷 P1090 合并果子
4.Codevs 1063 合并果子
题目描述
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。
输入输出格式
输入格式:
输入文件fruit.in包括两行,第一行是一个整数n(1<=n<=10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。
输出格式:
输出文件fruit.out包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于2^31。
输入输出样例
3 1 2 9
15
说明
对于30%的数据,保证有n<=1000:
对于50%的数据,保证有n<=5000;
对于全部的数据,保证有n<=10000。
思路:
用建一个小根堆,每次都取上面最小的两个加起来,插入堆中,每次取最小值,最后的值就为最小值。
╭(╯3╰)╮ 代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<algorithm> 5 using namespace std; 6 7 long long n,a,ans; 8 priority_queue<int>d; 9 10 int main() { 11 cin>>n; 12 for(int i=1; i<=n; i++) { 13 cin>>a; 14 d.push(-a); 15 } 16 int firs,secon; 17 for(int i=1; i<n;i++) { 18 firs=d.top(); 19 d.pop(); 20 secon=d.top(); 21 d.pop(); 22 d.push(firs+secon); 23 ans+=firs+secon; 24 } 25 cout<<-ans<<endl; 26 return 0; 27 }
5.洛谷 P1809 过河问题_NOI导刊2011提高(01)
题目描述
有一个大晴天,Oliver与同学们一共N人出游,他们走到一条河的东岸边,想要过河到西岸。而东岸边有一条小船。
船太小了,一次只能乘坐两人。每个人都有一个渡河时间T,船划到对岸的时间等于船上渡河时间较长的人所用时间。
现在已知N个人的渡河时间T,Oliver想要你告诉他,他们最少要花费多少时间,才能使所有人都过河。
注意,只有船在东岸(西岸)的人才能坐上船划到对岸。
输入输出格式
输入格式:
输入文件第一行为人数N,以下有N行,每行一个数。
第i+1行的数为第i个人的渡河时间。
输出格式:
输出文件仅包含一个数,表示所有人都渡过河的最少渡河时间
输入输出样例
4 6 7 10 15
42
说明
[数据范围]
对于40%的数据满足N≤8。
对于100%的数据满足N≤100000。
[样例解释]
初始:东岸{1,2,3,4},西岸{}
第一次:东岸{3,4},西岸{1,2} 时间7 第二次:东岸{1,3,4},西岸{2} 时间6 第三次:东岸{1},西岸{2,3,4},时间15 第四次:东岸{1,2},西岸{3,4} 时间7 第五次:东岸{},西岸{1,2,3,4} 时间7
所以总时间为7+6+15+7+7=42,没有比这个更优的方案。
思路:
方法有两种,一种是让最快的跑腿,第一快和第二快的一起过去,让最快的来回接送其他人,这样貌似是最优的,但还有一种情况,就是第一快和第二快过去了,让第一快回来,但不让它回去,让第一慢和第二慢回去,再让第二快回来接第一快,在某些情况下这种情况是比上一种快的,所以我们在两者之间取小,还有就是在只有1、2、3个人时,自己分别判断一下。
╭(╯3╰)╮ 代码:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 int n,ans,sped[100001]; 6 int start; 7 8 int main() { 9 cin>>n; 10 for(int i=1;i<=n;i++) { 11 cin>>sped[i]; 12 } 13 start = n; 14 while(start) { 15 if(start==1) { ans+=sped[1];break; } 16 else if(start==2) { ans+=sped[2]; break; } 17 else if(start==3) { ans+=sped[1]+sped[2]+sped[3]; break; } 18 else { 19 ans+=min(sped[2]+sped[1]+sped[start]+sped[2],sped[start]+2*sped[1]+sped[start-1]); 20 start-=2; 21 } 22 } 23 cout<<ans<<endl; 24 return 0; 25 }
自己选的路,跪着也要走完!!
以上是关于贪心算法的主要内容,如果未能解决你的问题,请参考以下文章