贪心算法:
贪心法就是遵循某种规则(这个规则可以有很多种,但是得出来的贪心算法不一定是对的),不断贪心地选取当前最优策略的算法设计方法。而DFS,BFS,动态规划是从某个状态不断的找,而贪心是按照一定规律去找。
例题1:
分析:
很典型的贪心算法问题,读完题后的第一感觉就是,若要让硬币用的最少,那么优先把面值大的硬币用完。
代码:
#include<bits/stdc++.h>
using namespace std;
const int v[6]={1,5,10,50,100,500};//硬币面值
int C[6];//硬币数量
int A;//支付的钱
int use[6]={0,0,0,0,0,0};//初始使用0张
int counts=0;//总共使用次数
void solve()
{
for(int i=5;i>=0;i--)
{
int n=A/v[i]<C[i]? A/v[i]:C[i];//用的硬币数量 不能超出最大值
use[i]=n; //对应面值使用数量
A= A-n*v[i];//还剩多少钱没支付
counts+=n; //使用总数
}
cout<<counts<<"(";
for(int i=5;i>=0;i--)
{
if(use[i])
{
printf("%d元硬币%d枚,",v[i],use[i]);
}
}
printf("合计%d枚)",counts);
}
int main()
{
for(int i=0;i<6;i++)
{
cin>>C[i];
}
cin>>A;
solve();
}
例题2:
分析:
也是个贪心问题,但是贪心的方法感觉有好几种比如1.优先选开始时间早的2.优先选结束时间最早的 3 优先选择用时最短的 2是正确的(脑补一下好像确实是,但是目前还没学会该怎么证明QAQ)
代码:
#include<bits/stdc++.h>
#define MAX 10000
using namespace std;
int n;//工作数量
int s[MAX],t[MAX];//开始与结束 的时间
pair<int,int> itv[MAX];
void solve()
{
for(int i=0;i<n;i++)
{
itv[i].first=t[i];//因为是优先取结束时间最早的 所以把结束时间存为first 方便用sort排序
itv[i].second=s[i];
}
sort(itv,itv+n);//排序
int res=0,t=0;//t记录的是当前可以工作的最早时间
for(int i=0;i<n;i++)
{
if(itv[i].second>t)
{
res++;
t=itv[i].first;//将当前最早时间替换成选择工作的结束时间
}
}
printf("%d\n",res);
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>s[i];
cin>>t[i];
}
solve();
}
例题3:
分析:
按照贪心的思想每次都选最小的,如果相同的话就往里面选小的。。依次类推,所以可以通过比较正向的字典序和反向的字典序来选。
代码:
#include<bits/stdc++.h>
#define MAX 999
typedef long long ll;
using namespace std;
int N;
char s[MAX];
void solve()
{
int a=0;
int b=N-1;
while(a<=b)
{
bool left=false;//左小还是右小
for(int i=0;a+i<=b;i++)
{
if(s[a+i]<s[b-i])
{
left=true;
break;
}
else if(s[a+i]>s[b-i])
{
left=false;
break;
}
}
if(left) putchar(s[a++]);
else putchar(s[b--]);
}
}
int main()
{
cin>>N;
cin>>s;
solve();
}
例题4
p
分析:
贪心规则:用一个半径R的距离尽可能的包括更多点。从最左边的点(未被标记)开始,找到R范围内能包括的最远的点,这个点就是标记点,然后从这个点开始,从左找到R范围内能包括的最远的点,至此,一个范围已经找出来了。然后再从刚才那个范围最右边的点的下一个开始反复 直到到达最后一个点。
代码:
#include<bits/stdc++.h>
#define MAX 999
typedef long long ll;
using namespace std;
int N;
int R;
int X[MAX];
void solve()
{
sort(X,X+N);
int p=0;
int counts=0;
while(p<N)
{
int biao;
for(biao=p;biao<N;biao++)
{
if(X[p]+R<X[biao])
break;
}
biao=biao-1;
counts++;
for(p=biao;p<N;p++)
{
if(X[biao]+R<X[p])
break;
}
}
cout<<counts;
}
int main()
{
cin>>N;
cin>>R;
for(int i=0;i<N;i++)
cin>>X[i];
solve();
}
例题5
分析:
可以类比哈夫曼树,相当于最后切割出来的每块板子,在之前每一轮切割中都会被计算一次,所以每块板子提供的开销为板子长度× 切割次数,所以问题可以归结为,求每块板子长度(叶子结点)×切割次数(深度)的和的最小值。
代码:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
int N,L[99999];
void solve()
{
ll ans=0;
while(N>1)//直到只剩下一个木板(初始)
{
int fmin=0,smin=1;//默认第一小和第二小是第一个板和第二个板
if(L[fmin]>L[smin])
{
int temp=fmin;
fmin=smin;
smin=temp;
}//如果不是就交换
for(int i=2;i<N;i++)
{
if(L[i]<fmin)
{
smin=fmin;
fmin=i;//如果比最小的小
}
else if(L[i]<smin)
{
smin=i;//如果比次小的小
}
}
int t =L[fmin]+L[smin];
ans +=t;
if(fmin==N-1)
{
int temp=fmin;
fmin=smin;
smin=temp;
}
L[fmin]=t;
L[smin]=L[N-1];
N--;
}
printf("%lld\n",ans);
}
int main()
{
cin>>N;
for(int i=0;i<N;i++)
{
cin>>L[i];
}
solve();
}