[BZOJ2832][宅男小C][模拟退火]

50 篇文章 0 订阅
1 篇文章 0 订阅

[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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值