通过正确的贪心策略来实现程序的最优解。
1.微扰
2.范围缩放
3.决策包容性
4.反证法
5.数学归纳法
做法:将每个游戏的时间段和钱数用结构体存储,以扣去的钱数为第一关键字进行从大到小排序; 把i从1-n进行枚举,再用j从time[i]到1进行遍历,若当前的时间没有被占用,则将该时间标记。若time[i]到1的时间全部被占用,则在总钱数中扣去对应的钱数即可。
证明:对于一个游戏,我们当然希望它玩得越迟越好,这样既可以少占用别的游戏所需要的时间,又可以尽可能少的捡钱。
主要代码如下:
inline void work() {
for(int i=1;i<=n;i++) {
int j;
for(j=a[i].time;j;j--)
if(!vis[j]) {
vis[j]=1;
break;
}
if(!j) m-=a[i].money;
}
}
做法:将n个纪念品的价值从小到大进行排序,同时枚举左右端点,若左端点的价格+右端点的价格已经超过w,则将右端点左移,同时将分组数+1.若左右端点和<=w,则分别将左右端点向中移,分组数+1.
证明:当当前区间内价值最小的纪念品与价值最大的纪念品的和超过w时,说明那个价值最大的纪念品和任何纪念品分都不可能不超过w,所以将它自成一组;不超过w时,枚举下一组即可。
主要代码如下:
int i=1;j=m;
while(i<=j)
{
if(a[i]+a[j]<=n) i++,j--,sum++;
else j--,sum++;
}
大致题意:有n个数的序列,这n个数只由1、2、3组成,可以将序列中的任意两个数进行交换,求将这个序列排成升序所需的最少次数。
做法:先从应该是2的位置开始枚举,每有一个1,就在1的区域去寻找2,找到就交换,并把次数+1;若没有2,则与3交换,将次数+1.再从应该是3的位置开始枚举,每有一个1,就在1的区域去寻找3,找到就交换,并把次数+1;若没有3,则与2交换,将次数+1.最后将错位的2、3归位。
证明:为了保证做法正确,需要先将1全部归位,而在将1归位的同时,也要先将被交换数尽可能的交换到要求的区间中去,若交换数的区间中已不符合要求,再将另一数进行交换。
部分代码如下:
inline void work() { //one,two,three分别指1、2、3的个数
for(int i=one+1;i<=one+two;i++)
if(a[i]==1) {
bool flag=0;
for(int j=1;j<=one;j++)
if(a[j]==2) {
swap(a[i],a[j]);
sum++;
flag=1;
break;
}
if(!flag)
for(int j=1;j<=one;j++)
if(a[j]==3) {
swap(a[i],a[j]);
sum++;
break;
}
}
for(int i=one+two+1;i<=one+two+three;i++)
if(a[i]==1) {
bool flag=0;
for(int j=1;j<=one;j++)
if(a[j]==3) {
swap(a[i],a[j]);
sum++;
flag=1;
break;
}
if(!flag)
for(int j=1;j<=one;j++)
if(a[j]==2) {
swap(a[i],a[j]);
sum++;
break;
}
}
for(int i=one+1;i<=one+two;i++)
if(a[i]==3) sum++;
}
我们按照如上做法,可以将代码简化。
做法:找出不正确的1、2、3,将不正确的1的个数与2的位置中的3与3的位置中的2的最大值相加。
代码如下:
#include
using namespace std;
int n,x=0,y=0,z=0,sum=0,t1=0,t2=0;
int a[1001];
inline int read() {
int s=0;char c=getchar();
while(c>='0' && c<='9') s=s*10+c-48,c=getchar();
return s;
}
inline void init() {
n=read();
for(int i=1;i<=n;i++) {
a[i]=read();
if(a[i]==1) x++;
if(a[i]==2) y++;
if(a[i]==3) z++;
}
}
inline void work() {
for(int i=1;i<=x;i++) if(a[i]!=1) sum++;
for(int i=x+1;i<=x+y;i++) if(a[i]==3) t1++;
for(int i=x+y+1;i<=x+y+z;i++) if(a[i]==2) t2++;
}
inline void outo() {
printf("%d\n",sum+max(t1,t2));
}
int main() {
init();
work();
outo();
return 0;
}
大致题意:共有S个牛棚,只有C个牛棚中有牛,需要将C个有牛的牛棚的顶补好,使用的模板数不能超过M,求这些模板的最小长度和。
做法:刚开始用一块木板,长度和为S,随后将两端没有牛的牛棚数减去,再从没有标记中的区域寻找最大的没有被标记的空位,将它减去,直到达到了最大的木板数。
证明:将空位最大到第m大的牛棚数减去,就保证了最小的长度和。(我感觉这很容易理解就不多解释了)
代码如下:
#include
using namespace std;
int M,S,C,sum;
int a[201];
inline int read() {
int s=0;char c=getchar();
while(c>='0' && c<='9') s=s*10+c-48,c=getchar();
return s;
}
inline void init() {
M=read(),S=read(),C=read();
sum=S;
for(int i=1;i<=C;i++) a[i]=read();
sort(a+1,a+C+1);
}
inline bool cmp(int x,int y) {
return x>y;
}
int b[201];
inline void work() {
sum-=(a[1]-1);
sum-=(S-a[C]);
for(int i=1;i