简要分析题目
- 题目链接(AcWing)
- 题目链接(洛谷)
刚看到题时我觉得很简单,每天都买涨幅最大的就好了,就是道很简单的贪心嘛,然后看了一下别人的题解,用的全是DP,一看就是逊啦!
贪心思路
处理每天的价格
由于每行输入代表一天的物价,所以可以把问题分到每一天,每输入一天物价处理一次。
首先用了一个类表示每天的价格和第二天的上涨量(也可能是下跌):
class ITEM {//将涨价单独列出方便排序
public:
int id, value;
ITEM() :id(0), value(0) {}
inline void update(int i, int v) { id = i; value = v; return; }
};
inline bool cmp(ITEM, ITEM);//声明cmp函数
class DAY {
int* price;//物品种类数不确定,用动态数组避免空间浪费
ITEM* rise;
public:
friend class WEI;//后面将小伟抽象为一个类,设为友元类方便WEI中成员函数访问
friend inline bool cmp(ITEM,ITEM);//将cmp函数设为友元函数
DAY() {}
~DAY() {//析构函数,销毁对象时将对象申请的内存释放
delete[]price;释放price指向的内存
delete[]rise;释放rise指向的内存
}
inline void set() {初始化(第一天时没有前一天的价格来衡量涨价)
price = new int[n];
for (int i = 0; i < n; i++)price[i] = read();
rise = new ITEM[n];
}
void update() {//每天更新价格
int x;
for (int i = 0; i < n; i++)price[rise[i].id] += rise[i].value;//根据昨天物价和涨价计算今天物价
for (int i = 0; i < n; i++) {
x = read();
rise[i].update(i, x - price[i]);//根据明天物价和今天物价计算涨价
}
sort(rise, rise + n, cmp);根据涨幅排序
}
}day_data;
于是就可以利用 update函数每天更新。
处理每天 -阿玮- 小伟的买卖行为
我们写出上文提到的小伟类,将买和卖统一,每次购买后就换算为第二天金钱,省去处理物品数量.
class WEI {
int money;//小伟金钱
public:
WEI() :money(0) {}
inline void give_money(int x = read()) { money = x; return; }//初始化小伟金钱
void update(const DAY& d = day_data) {//更新小伟的买卖行为后的金钱
int temp = 0;
for (int i = 0; day_data.rise[i].value > 0 && i < n && money; i++) {
temp += (money / d.price[d.rise[i].id]) * (d.rise[i].value +
d.price[d.rise[i].id]);//每次买完物品立即换算成第二天的价格存入temp
money %= d.price[d.rise[i].id];//以今天价格扣除金钱
}
money += temp; return;剩余金钱和交易所得金钱相加得到最终金钱
}
inline void print() { printf("%d", money); return; }//输出小伟金钱
}_wei;
模拟
根据题意直接模拟即可:
int main() {
t = read() - 1;
n = read();
_wei.give_money();
day_data.set();
for (int i = 0; i < t; i++) {
day_data.update();
_wei.update();
}
_wei.print();
return 0;
}
贪心思路完整代码
代码如下:
#include
#include
#include
using namespace std;
int t, n;
inline int read() {
int x = 0;
char ch = getchar();
while (ch > '9' || ch < '0')ch = getchar();
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch - 48);
ch = getchar();
}
return x;
}
class ITEM {
public:
int id, value;
ITEM() :id(0), value(0) {}
inline void update(int i, int v) { id = i; value = v; return; }
};
inline bool cmp(ITEM, ITEM);
class DAY {
int* price;
ITEM* rise;
public:
friend class WEI;
friend inline bool cmp(ITEM,ITEM);
DAY() {}
~DAY() {
delete[]price;
delete[]rise;
}
inline void set() {
price = new int[n];
for (int i = 0; i < n; i++)price[i] = read();
rise = new ITEM[n];
}
void update() {
int x;
for (int i = 0; i < n; i++)price[rise[i].id] += rise[i].value;
for (int i = 0; i < n; i++) {
x = read();
rise[i].update(i, x - price[i]);
}
sort(rise, rise + n, cmp);
}
}day_data;
inline bool cmp(ITEM a, ITEM b) {
return a.value ^ b.value ? a.value > b.value:day_data.price[a.id]
< day_data.price[b.id];
}
class WEI {
int money;
public:
WEI() :money(0) {}
inline void give_money(int x = read()) { money = x; return; }
void update(const DAY& d = day_data) {
int temp = 0;
for (int i = 0; day_data.rise[i].value > 0 && i < n && money; i++) {
temp += (money / d.price[d.rise[i].id]) * (d.rise[i].value +
d.price[d.rise[i].id]);
money %= d.price[d.rise[i].id];
}
money += temp; return;
}
inline void print() { printf("%d", money); return; }
}_wei;
int main() {
t = read() - 1;
n = read();
_wei.give_money();
day_data.set();
for (int i = 0; i < t; i++) {
day_data.update();
_wei.update();
}
_wei.print();
return 0;
}
错误
这个代码只得了35分(洛谷),简单分析一下原因吧:
- 不应该用上涨的量排序,应该用上涨的比例排序。
- 金钱和价格不一定整除(大部分时候都不会整除),可能造成浪费。
背包问题
所以其实这道题应该按照背包问题来做,每一天都以完全背包问题的方法处理一次(
--所以人家的DP才是正解是吧,所以你的贪心其实本来就是错的,只能骗分?
--读书人的代码能叫骗分吗?这叫先拿部分分!)
核心更改
问题出在小伟的购买方式上,所以只要修改小伟的 update函数即可:
void update(const DAY& d = day_data) {
int* temp = new int[money + 1];
for (int i = 0; i < n; i++) {
for (int j = d.price[i]; j <= n; j++) {
temp[j] = max(temp[j], temp[j - d.price[i]] + d.rise[i]);//背包问题状态转移方程
}
}
}
然后由于已经不需要从涨幅高到涨幅低购买,所以对涨幅排序已经不必要了,同时 ITEM 类也不需要了:
class DAY {
int* price,* rise;
public:
friend class WEI;
DAY():price(0),rise(0){}
~DAY() {
delete[]price;
delete[]rise;
}
void set() {
price = new int[n];
for (int i = 0; i < n; i++)price[i] = read();
rise = new int[n];
memset(rise, 0, n * 4);
}
void update() {
for (int i = 0; i < n; i++) {
price[i] += rise[i];
rise[i] = read() - price[i];
}
return;
}
}day_data;
稍作优化
这个代码已经可以AC了,但本着精(shu)益(ju)求(hao)精(kan)的原则,我们还要进行一点(负)优化。
让小伟在购买时可以对每一种物品进行判断,将不会涨价的物品直接排除掉,只要加一个 if 语句即可:
void update(const DAY& d = day_data) {
int* temp = new int[money + 1];
memset(temp, 0, (money + 1) * 4);
for (int i = 0; i < n; i++) {
if (d.rise[i] > 0){//若涨价量为正
for (int j = d.price[i]; j <= money; j++) {
temp[j] = max(temp[j], temp[j - d.price[i]] + d.rise[i]);
}
}
}
money += temp[money];
}
这样就省去了一部分循环,在洛谷上实测优化前(开启O2优化)s用时在804~855ms之间,
优化后(开启O2优化)是在584~586ms之间,可以说是优化了很多。
总结
这道题其实就是完全背包问题,只要不想偏 -(说的就是我)- 基本都会做,需要思考的地方就是如何发现它是一个完全背包问题 -(而不是贪心)- 。当然如果有的人不会DP就另当别论了 -(不会DP去比什么赛呢)-。
完整代码
代码如下:
#include
#include
#include
using namespace std;
int t, n;
inline int max(int a, int b) {
return a > b ? a : b;
}
inline int read() {
int x = 0;
char ch = getchar();
while (ch > '9' || ch < '0')ch = getchar();
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch - 48);
ch = getchar();
}
return x;
}
class DAY {
int* price,* rise;
public:
friend class WEI;
DAY():price(0),rise(0){}
~DAY() {
delete[]price;
delete[]rise;
}
void set() {
price = new int[n];
for (int i = 0; i < n; i++)price[i] = read();
rise = new int[n];
memset(rise, 0, n * 4);
}
void update() {
for (int i = 0; i < n; i++) {
price[i] += rise[i];
rise[i] = read() - price[i];
}
return;
}
}day_data;
class WEI {
int money;
public:
WEI() :money(0) {}
inline void give_money(int x = read()) { money = x; return; }
void update(const DAY& d = day_data) {
int* temp = new int[money + 1];
memset(temp, 0, (money + 1) * 4);
for (int i = 0; i < n; i++) {
if (d.rise[i] > 0)for (int j = d.price[i]; j <= money; j++) {
temp[j] = max(temp[j], temp[j - d.price[i]] + d.rise[i]);
}
}
money += temp[money];
}
inline void print() { printf("%d", money); return; }
}_wei;
int main() {
t = read() - 1;
n = read();
_wei.give_money();
day_data.set();
for (int i = 0; i < t; i++) {
day_data.update();
_wei.update();
}
_wei.print();
return 0;
}