[BZOJ2832][宅男小C][模拟退火]
题目大意:
众所周知,小C是个宅男,所以他的每天的食物要靠外卖来解决。小C现在有M元钱,他想知道这些钱他最多可以吃多少天。
餐厅提供N种食物,每种食物有两个属性,单价Pi和保质期Si,表示小C需要花Pi元才能买到足够一天吃的这种食物,并且需要在送到Si天内吃完,否则食物会变质,就不能吃了,若Si为0则意味着必须在送到当天吃完。另外,每次送餐需要额外F元送餐费。
思路:
对小C要订多少次餐模拟退火一下。
随机大法好!
代码:
#include <bits/stdc++.h>
const int Maxn = 10005;
using namespace std;
typedef long long ll;
int n;
ll tot, F, ans;
inline double ran(void) {
return rand() / (double) RAND_MAX;
}
struct Obj {
ll cost, day;
friend bool operator < (const Obj &a, const Obj &b) {
if (a.cost == b.cost) return a.day > b.day;
return a.cost < b.cost;
}
} o[Maxn];
inline void pre(void) {
ll d = -1;
int m = n; n = 0;
for (int i = 1; i <= m; i++) {
if (o[i].day > d) o[++n] = o[i], d = o[i].day;
}
}
inline ll j(ll t) {
if (t <= 0) return 0;
ll m = tot - t * F, res = 0, num, day = 0;
for (int i = 1; i <= n; i++) {
num = min(m / o[i].cost / t, o[i].day - day + 1);
m -= num * o[i].cost * t;
day += num;
res += t * num;
if (day <= o[i].day) {
num = m / o[i].cost;
res += num;
ans = max(ans, res);
return res;
}
}
ans = max(ans, res);
return res;
}
inline void SA(double T) {
ll Now = 1, A;
while (T >= 1) {
A = Now + (ll) (T * (ran() * 2 - 1));
if (A <= 0) A = T * ran();
ll dE = j(A) - j(Now);
if (dE > 0) Now = A;
T *= 0.93;
}
}
inline void solve(void) {
for (int i = 1; i <= n; i++)
scanf("%lld%lld", &o[i].cost, &o[i].day);
sort(o + 1, o + 1 + n);
pre();
ans = 0;
SA(tot / F + 1);
printf("%lld\n", ans);
}
int main(void) {
//freopen("in.txt", "r", stdin);
while (scanf("%lld%lld%d", &tot, &F, &n) != EOF) solve();
return 0;
}
完。
By g1n0st