这几天一直做题,越发感到,贪心并不是一种技巧,而更多的是一种思考方式。
从这几天的题目来看,一个需要贪心的题,如果不是没有想到具体怎么贪,那么难度会瞬间倍增,但是一旦想通了,就会发现这道题太简单了。
(不得不吐槽一句vj的提交题目的系统,C和D无论我怎么搞,都是submit failed,试了试其他的直接就可以判断出WA,我对这个系统真的是无语了)
下面来一类例题。
超市往外卖东西,给你物品的价值和物品售卖的截止时间,求卖得的最大利润。
这道题的贪心的思路是:
首先,假设所有的物品都是在截止日期卖出的。
那么首先对价格排序,让贵的东西在它截止的那天卖出。
如果按照价格排序顺下来,有稍便宜的物品与贵的物品的截止日期是同一天
那么就从截止日期开始向前提前,看哪一天是空闲的,接着,卖出物品。
下面是代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<algorithm>
#include<vector>
#include<cstring>
#define ll long long
using namespace std;
struct box //定义结构体
{
int pri;
int tim;
};
bool cmp(box x,box y) //按照价格大小排序
{
return x.pri>y.pri;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
int sum=0;
box a[100000];
for(int i=0;i<n;i++)
{
scanf("%d",&a[i].pri);
scanf("%d",&a[i].tim);
}
sort(a,a+n,cmp); //从这里开始是代码的核心部分
bool v[100000]; //定义bool数组,看哪一天是否卖出东西
memset(v,0,sizeof(v));
for(int i=0;i<n;i++)
{
if(!v[a[i].tim]) //如果空闲,那么卖出
{
sum+=a[i].pri;
v[a[i].tim]=true;
}
else //不空闲,向前追溯
{
for(int j=a[i].tim-1; j>=1; --j)
if(!v[j])
{
sum+=a[i].pri;
v[j] = true;
break;
}
}
}
cout<<sum<<endl; //输出
}
return 0;
}
还有一个与他相似的题目
主要内容是
给定作业的完成期限,和如果逾期所扣的分数,求最后最少扣的分数。
对于这个题目,与上个题目的思路是完全一样的。
首先,假设所有作业都在最后期限完成。
按照索扣分值排序,优先完成扣分值多的。
如果最后期限冲突,那么就往前追溯。
可以说两个题的模板是一抹一样的。
只不过计数方式有差异。
上一个需要计算的是能安排上的求和,这一个是按排不上的求和。
贴代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#include<iostream>
using namespace std;
struct Node
{
int d; //日期
int s; //分数
} data[1005];
int v[1005];
int cmp (Node a, Node b) //排序
{
if (a.s != b.s)
return a.s > b.s;
else return a.d < b.d;
}
int main ()
{
int c;
scanf ("%d", &c);
while (c--)
{
int n;
memset (v, 0, sizeof(v));
scanf ("%d", &n);
for (int i=0; i<n; ++i)
scanf ("%d", &data[i].d);
for (int i=0; i<n; ++i)
scanf ("%d", &data[i].s);
sort (data, data+n, cmp);
/*for(int i=0;i<n;i++)
{
cout<<data[i].d<<" "<<data[i].s<<endl;
}*/
int a= 0;
for (int i=0; i<n; ++i)
{
int j=data[i].d;
for (; j>=1; --j)
if(!v[j])
{
v[j] = 1;
break;
}
if(j == 0) //如果减到零还不能被安排上,那就扣分
a+= data[i].s;
}
printf ("%d\n", a);
}
return 0;
}