[USACO08NOV]Toys G 题解

17 篇文章 0 订阅
1 篇文章 0 订阅

[USACO08NOV]Toys G

首先可以网络流建图,但是因为 n n n 的范围很大,所以我们考虑换一种方法。

首先考虑网络流建图的时候我们是考虑拆点之后进行跑流量,对于所有满流的情况我们计算一个最小费用。


我们对于这种情况不妨进行二分答案之后进行判断,显然我们二分的不能直接是答案。我们考虑那个条件我们知道了之后可以进行贪心或者 D p \tt Dp Dp。总共就那么几个条件,只有购买了多少个是不知道的。那么我们就二分买了几个玩具。


简单思考可以发现对于买的玩具数量和花费是一个二次函数,下凸,进行三分。

具体贪心:

首先如果一个方案时间又短又优秀显然只用这个方案,不然两种方案肯定是一个时间短花费多,另一个反之。我们考虑用队列维护又多少没有消毒的,然后每次优先通过时间长的进行消毒,不然我们就使用时间短的进行消毒。

这里有一个问题如果放到了时间短的里面但是没有被使用,之后又可以放到时间长的里面了。

对于这个疑问,我们可以每次将时间短的来更新一下时间长的,之后每次将贡献放到被使用的时候计算即可。

#include <bits/stdc++.h>
using namespace std;

//#define Fread
//#define Getmod

#ifdef Fread
char buf[1 << 21], *iS, *iT;
#define gc() (iS == iT ? (iT = (iS = buf) + fread (buf, 1, 1 << 21, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
#define getchar gc
#endif // Fread

template <typename T>
void r1(T &x) {
	x = 0;
	char c(getchar());
	int f(1);
	for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(; '0' <= c && c <= '9';c = getchar()) x = (x * 10) + (c ^ 48);
	x *= f;
}

template <typename T,typename... Args> inline void r1(T& t, Args&... args) {
    r1(t);  r1(args...);
}

#ifdef Getmod
const int mod  = 1e9 + 7;
template <int mod>
struct typemod {
    int z;
    typemod(int a = 0) : z(a) {}
    inline int inc(int a,int b) const {return a += b - mod, a + ((a >> 31) & mod);}
    inline int dec(int a,int b) const {return a -= b, a + ((a >> 31) & mod);}
    inline int mul(int a,int b) const {return 1ll * a * b % mod;}
    typemod<mod> operator + (const typemod<mod> &x) const {return typemod(inc(z, x.z));}
    typemod<mod> operator - (const typemod<mod> &x) const {return typemod(dec(z, x.z));}
    typemod<mod> operator * (const typemod<mod> &x) const {return typemod(mul(z, x.z));}
    typemod<mod>& operator += (const typemod<mod> &x) {*this = *this + x; return *this;}
    typemod<mod>& operator -= (const typemod<mod> &x) {*this = *this - x; return *this;}
    typemod<mod>& operator *= (const typemod<mod> &x) {*this = *this * x; return *this;}
    int operator == (const typemod<mod> &x) const {return x.z == z;}
    int operator != (const typemod<mod> &x) const {return x.z != z;}
};
typedef typemod<mod> Tm;
#endif

//#define int long long
const int maxn = 1e5 + 5;
const int maxm = maxn << 1;

int n, v[maxn];

int fd, fc, sd, sc, Pr;

#define id first
#define val second
deque<pair<int, int>> cf, cs, vir;

const int inf = 1e9;

int calc(int x) {
    cf.clear(), cs.clear(), vir.clear();
    int ans = x * Pr;
    for(int i = 1; i <= n; ++ i) {
        while(!vir.empty() && vir.front().id <= i - fd) {
            cf.push_back(vir.front()), vir.pop_front();
        }
        while(!cf.empty() && cf.front().id <= i - sd) {
            cs.push_back(cf.front()), cf.pop_front();
        }
        int left(v[i]);
        int tmp = min(x, left);
        x -= tmp, left -= tmp;
        while(left && !cs.empty()) {
            tmp = min(left, cs.back().val);
            ans += tmp * sc, left -= tmp, cs.back().val -= tmp;
            if(!cs.back().val) cs.pop_back();
        }
//        printf("i = %d\n", i);
        while(left && !cf.empty()) {
//            printf("%d %d\n", left, cf.back().val);
            tmp = min(left, cf.back().val);
//            printf("tmp = %d\n", tmp);
            ans += tmp * fc, left -= tmp, cf.back().val -= tmp;
            if(!cf.back().val) cf.pop_back();
        }
        if(left) return inf;
        vir.push_back(make_pair(i, v[i]));
    }
    return ans;
}

int Work() {
    int l(0), r(0);
    for(int i = 1; i <= n; ++ i) r += v[i];
    while(r - l > 2) {
//        printf("(%d, %d)\n", l, r);
        int m1 = (l * 2 + r) / 3, m2 = (l + r * 2 + 2) / 3;
        int f1 = calc(m1);
//        puts("XXX");
        int f2 = calc(m2);
        if(f1 >= f2) l = m1;
        else r = m2;
    }
    int ans(inf);
    for(int i = l; i <= r; ++ i) ans = min(ans, calc(i));
    return ans;
}

signed main() {
//    freopen("S.in", "r", stdin);
//    freopen("S.out", "w", stdout);
    int i, j;
    r1(n);
    r1(fd, sd, fc, sc);
    if(fd > sd) {
        swap(fd, sd), swap(fc, sc);
    }
    if(sc > fc) {
        sc = fc, sd = fd;
    }
    r1(Pr);
    for(i = 1; i <= n; ++ i) r1(v[i]);
    printf("%d\n", Work());
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: p109 [noip2004 提高组] 合并果子: 这道题目是一道经典的贪心算法题目,题目大意是给定n个果子,每个果子的重量为wi,现在需要将这n个果子合并成一个果子,每次合并需要消耗的代价为合并的两个果子的重量之和,求最小的代价。 我们可以使用贪心算法来解决这个问题,每次选择两个最小的果子进行合并,然后将合并后的果子的重量加入到集合中,重复这个过程直到只剩下一个果子为止。 这个算法的正确性可以通过反证法来证明,假设存在一种更优的合并方案,那么这个方案一定会在某一步将两个比当前选择的两个更小的果子进行合并,这样就会得到一个更小的代价,与当前选择的方案矛盾。 usaco06nov fence repair: 这道题目是一道经典的贪心算法题目,题目大意是给定n个木板,每个木板的长度为li,现在需要将这n个木板拼接成一块长度为L的木板,每次拼接需要消耗的代价为拼接的两个木板的长度之和,求最小的代价。 我们可以使用贪心算法来解决这个问题,每次选择两个最小的木板进行拼接,然后将拼接后的木板的长度加入到集合中,重复这个过程直到只剩下一个木板为止。 这个算法的正确性可以通过反证法来证明,假设存在一种更优的拼接方案,那么这个方案一定会在某一步将两个比当前选择的两个更小的木板进行拼接,这样就会得到一个更小的代价,与当前选择的方案矛盾。 ### 回答2: 题目描述: 有n个果子需要合并,合并任意两个果子需要的代价为这两个果子的重量之和。现在有一台合并机器,可以将两个果子合并成一堆并计算代价。问将n个果子合并成一堆的最小代价。 这个问题可以用贪心算法来解决,我们可以使用一个最小堆来存储所有果子的重量。每次从最小堆中取出两个最小的果子,将它们合并成为一堆,并将代价加入答案中,将新堆的重量加入最小堆中。重复以上步骤,直到最小堆中只剩下一堆为止。这样得到的代价就是最小的。 证明如下: 假设最小堆中的果子按照重量从小到大依次为a1, a2, ..., an。我们按照贪心策略,每次都将重量最小的两个果子合并成为一堆,设合并的过程为b1, b2, ..., bn-1。因此,可以发现,序列b1, b2, ..., bn-1必然是一个前缀和为a1, a2, ..., an的 Huffman 树变形。根据哈夫曼树的定义,这个树必然是最优的,能够得到的代价最小。 因此,使用贪心策略得到的答案必然是最优的,而且时间复杂度为O(n log n)。 对于[usaco06nov] fence repair g这道题,其实也可以用相同的思路来解决。将所有木板的长度存储在一个最小堆中,每次取出最小的两个木板长度进行合并,代价即为这两个木板的长度之和,并将合并后木板的长度加入最小堆中。重复以上步骤,直到最小堆中只剩下一块木板。得到的代价就是最小的。 因此,贪心算法是解决这类问题的一种高效、简单但有效的方法,可以应用于很多有贪心性质的问题中。 ### 回答3: 这两个题目都需要对操作进行模拟。 首先是合并果子。这个题目先将所有果子放进一个优先队列中。每次取出来两个果子进行合并,直到只剩下一个果子即为答案。合并的代价为两个果子重量之和。每次合并完之后再将新的果子放入优先队列中,重复上述过程即可。 再来看fence repair。这个题目需要用到贪心和并查集的思想。首先将所有板子的长度放入一个最小堆中,每次取出堆顶元素即为最短的板子,将其与其相邻的板子进行合并,合并的长度为这两块板子的长度之和。操作完之后再将新的板子长度放入最小堆中,重复上述过程直到只剩下一块板子。 关于合并操作,可以使用并查集来实现。维护每个板子所在的集合,每次操作时合并两个集合即可。 最后,需要注意的是题目中给出的整数都很大,需要使用long long来存储避免溢出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值