主要是强调当前这个状态,从这个状态里寻找最优解,从而得出最终解。
同时也引出了运用此算法的条件(前提):
如何判断一道题是否需要此算法,我们需要去证明从每个子集中解能够得出最优解(即最终解)。
此外,关于子集解的划分求,一种方式可以从数据入手,在数据中寻找特殊数据,如最大值最小值,起始值等。
问题概述:现存n个物体,其中每个物体重量为Wi,每个物体的价值为Pi,在总质量不超过C的情况下尽可能使总价值高。每个物品可取部分。
输入:
n C
w1 p1
w2 p2
· ·
· ·
wn pn
输出:
最大价值k
分析:首先若考虑从最大价值的物品开始取,可能其重量也大,则最终取的总重量在C之内的物品可能少,其K值也不一定是最大;若从重量最小的取,可能其价值最小,使之更不满足K的最大化。综合考虑,可以将数据依据 Gi(价值/重量)的大小排序,优先选择Gi大的,直到价值sum=>C为止。
代码如下
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
struct thing{
int zl;
int jz;
double g;
};
struct thing t[20];
bool cmp(thing x,thing y)
{
return x.g<y.g;
}
int main(void)
{
int n,C;
scanf("%d%d",&n,&C);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&t[i].zl,&t[i].jz);
t[i].g=(t[i].jz*1.0/t[i].zl);
}
sort(t,t+n,cmp);
double sum=0;
int G=0;
int j;
for(j=1;j<=n;j++)
{
sum+=t[j].jz;
G+=t[j].zl;
if(G+t[j+1].zl>=C)
{
j++;
break;
}
}
sum+=((C-G)*t[j].g);
cout<<sum;
return 0;
}
问题:数轴上有n个开区间( a i , b i ) 。选择尽量多个区间,使得这些区间没有公共点。
分析:考虑时间长度,时间起始点。
我们需要分析两个区间a,b的关系:
代码如下(示例):
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
struct qj{
int st;
int end;
};
struct qj t[20];
bool cmp(qj x,qj y)
{
return x.end<y.end;
}
int main(void)
{
int n;
scanf("%d",&n);
int cnt=0;
for(int i=0;i<n;i++)
{
scanf("%d%d",&t[i].st,&t[i].end);
}
sort(t,t+n,cmp);
int E=0;
for(int i=0;i<n;i++)
{
if(t[i].st>E)
{
cnt++;
E=t[i].end;
}
}
cout<<cnt;
return 0;
}
提示:选择贪心算法一定要确保题目能使局部最优为解
如上述问题一中,若纸币为11,5,1,总面额为15时,如果用上面算法则答案为11,1,1,1,1,但是该情况下最优解应该是5,5,5。所以不满足题意。
因此在用贪心时一定要提前确保局部解为最优解。
1,Elephant
问题:
An elephant decided to visit his friend. It turned out that the elephant’s house is located at point 0 and his friend’s house is located at point x(x > 0) of the coordinate line. In one step the elephant can move 1, 2, 3, 4 or 5 positions forward. Determine, what is the minimum number of steps he need to make in order to get to his friend’s house.
input:
The first line of the input contains an integer x (1 ≤ x ≤ 1 000 000) — The coordinate of the friend’s house.
output:
Print the minimum number of steps that elephant needs to make to get from point 0 to point x.
关键在于顺序贪心
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
int main(void)
{
int a[5]={
5,4,3,2,1};//要素1*
int p;
cin>>p;
int ans=0;
int sum=0;
for(int i=0;i<5;i++)
{
while(sum<p)//体现*
{
sum+=a[i];
ans++;
}
}
cout<<ans;
return 0;
}
关键在于选择的顺序,我们需要从最大的6x6个数开始,再依次选择4x4,3x3,最后再去看剩余空间中可以供2x2的数,最后看1x1.
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
int a[10];
int a_[4]={
0,5,3,1};//表示a[3]剩余量中可以包含2*2的盒子数
int main()
{
while(1)
{
int s=0;
for(int i=1;i<7;i++)
{
scanf("%d",&a[i]);
s+=a[i];
}
if(s==0) break;
int sum=a[6]+a[5]+a[4]+(a[3]+3)/4;
int a3s=a[3]%4; //a3s表示a[3]多出来的个数;
int a2=a[2]-a_[a3s]-a[4]*5;//a2表示后面剩余的盒子数;
if(a2>0) sum+=(a2+8)/9;
int a1=a[1]-(sum*36-a[6]*36-a[5]*25-a[4]*16-a[3]*9-a[2]*4);
if(a1>0) sum+=(a1+35)/36;
cout<<sum<<endl;
}
return 0;
}
3,Minimize the Permutation
此题考的是置换,每个位置只能与后面的数字换一次,先从最后的数字开始换,使小数字尽量在前面,再从第一个数字换(交换的前提是该位置第一次换的时候没有调整过)
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
int a[500];
int b[500];
int main(void)
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
for(int i=n-1;i>0;i--)
{
if(a[i]<a[i-1])
{
int y=a[i];
a[i]=a[i-1];
a[i-1]=y;
b[i-1]=1;
}
}
for(int i=0;i<n-1;i++)
{
if(a[i]>a[i+1]&&b[i]==0)
{
int y=a[i];
a[i]=a[i+1];
a[i+1]=y;
}
}
for(int i=0;i<n;i++)
{
b[i]=0;
cout<<a[i]<<' ';
}
cout<<endl;
}
return 0;
}
4,Car
关键在于如何把每一段的速度V求取出来。
这里需要注意的是我们为确保时间t为整数,所以每段的v应该为double值,此外贪心体现在从最后一段。
(即该段速度最大的一段开始求,使t总最小,所以最后一段t‘最小为1s,所以v’=s’)。
倒推得出每一段的V(min_t)。需要注意的是该题的难度在于如何求v。例如若s1,s2,s3分别为2,3,2.则易出v3=2m/s,t3=1s;=> v3>s2,所以v2!=s2,这里求v2划重点。
v2=v=(double(s)/(int(double(s-max)/v)+1));
分解v2=>double(s)是把s变小数,相当于s*1.0,
t=(int(double(s-max)/v)+1);=>s-max主要是考虑到s/v是整除的情况下,能防止t+1浪费;
=>double(s-max)/v+1,当s/v不能整除时,tmax=(s/v)+1;
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
#define max (1e-8)
int a[200001];
int s;
double v;
int k=1;
int main(void)
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
ll ts=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
v=a[n]-a[n-1];
ts=1;
// cout<<"v="<
for(int i=n-1;i>=1;i--)
{
s=a[i]-a[i-1];
if(s<=v)
{
v=s;
ts++;
}else
{
ts+=(int(double(s-max)/v)+1);
v=(double(s)/(int(double(s-max)/v)+1));
}
//cout<<"v="<
}
printf("Case #%d: %d\n",k++,ts);
}
return 0;
}