「BZOJ1229」[USACO2008 Nov] toy 玩具
Description
玩具 [Chen Hu, 2006] Bessie的生日快到了, 她希望用D (1 <= D <= 100,000; 70%的测试数据都满足 1 <= D <= 500)天来庆祝. 奶牛们的注意力不会太集中, 因此Bessie想通过提供玩具的方式来使它们高兴. 她已经计算出了第i天需要的玩具数T_i (1 <= T_i <= 50). Bessie的幼儿园提供了许多服务给它们的奶牛程序员们, 包括一个每天以Tc (1 <= Tc <= 60)美元卖出商品的玩具店. Bessie想尽可能的节省钱, 但是Farmer John担心没有经过消毒的玩具会带来传染病(玩具店卖出的玩具是经过消毒的). 有两种消毒的方式. 第1种方式需要收费C1美元, 需要N1个晚上的时间; 第2种方式需要收费 C2美元, 需要N2个晚上的时间(1 <= N1 <= D; 1 <= N2 <= D; 1 <= C1 <= 60; 1 <= C2 <= 60). Bessie在party结束之后把她的玩具带去消毒. 如果消毒只需要一天, 那么第二天就可以拿到; 如果还需要一天, 那么第三天才可以拿到. 作为一个受过教育的奶牛, Bessie已经了解到节约的意义. 帮助她找到提供玩具的最便宜的方法.
Input
* 第 1 行: 六个用空格隔开的整数 D, N1, N2, C1, C2, Tc
* 第 2..D+1 行: 第 i+1 行包含一个整数: T_i
Output
第 1 行: 提供玩具所需要的最小费用.
Sample Input
8
2
1
6
输入解释:
Bessie想开4天的party, 第1天需要8个玩具, 第2天需要2个玩具, 第3天需要1个玩具,
第4天需要6个玩具. 第一种方式需要$2, 用时1天; 第二种方式需要$1, 用时2天. 买
一个玩具需要$3.
Sample Output
输出解释:
第 1 天 买8个玩具, 花去$24; 送2个玩具去快洗, 6个慢洗.
第 2 天 取回2个快洗的玩具, 花去$4. 送1个玩具去慢洗.
第 3 天 取回6个慢洗的玩具, 花去$6.
第 4 天 取回所有的玩具(与现有的加在一起正好6个), 花去$1. 这样就用了最少的钱.
题解
如果D比较小,这题就是经典的费用流模型(餐巾计划问题)。但是D很大,这个做法明显会超时。重新来看费用流,单位费用随流量的增加而减少。也就是费用是个凸函数!
而流量恰好是玩具个数。
三分玩具总数+贪心求出费用!
贪心维护几个队列
快洗的,慢洗的,其它
优先用慢洗的,然后是最近时间快洗的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
#include<map> #include<set> #include<cmath> #include<deque> #include<cstdio> #include<cstring> #include<iostream> #define mp(x,y) make_pair(x,y) #define ll long long #define inf 1000000000 using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,t1,t2,c1,c2,tc; int d[100005],sum; deque<pair<int,int> >sl,qk,tmp; int f(int x) { sl.clear();qk.clear();tmp.clear(); int ans=(tc-c2)*x; sl.push_back(mp(0,x)); for(int i=1;i<=n;i++) { while(!tmp.empty()&&i-tmp.front().first>=t1) qk.push_back(tmp.front()),tmp.pop_front(); while(!qk.empty()&&i-qk.front().first>=t2) sl.push_back(qk.front()),qk.pop_front(); int T=d[i]; while(T>0) { if(!sl.empty()) { if(sl.back().second>T) { sl.back().second-=T; ans+=T*c2;T=0; } else { T-=sl.back().second; ans+=sl.back().second*c2;sl.pop_back(); } } else if(!qk.empty()) { if(qk.back().second>T) { qk.back().second-=T; ans+=T*c1;T=0; } else { T-=qk.back().second; ans+=qk.back().second*c1;qk.pop_back(); } } else return inf; } tmp.push_back(mp(i,d[i])); } return ans; } int main() { n=read();t1=read();t2=read();c1=read();c2=read();tc=read(); for(int i=1;i<=n;i++)d[i]=read(),sum+=d[i]; if(t1>t2)swap(t1,t2),swap(c1,c2); if(c1<c2)c2=c1; int l=0,r=sum; while(1) { if(r-l<=5) { int t=f(l); for(int i=l+1;i<r;i++)t=min(t,f(i)); printf("%d\n",t); return 0; } int x=l+(r-l)/2,y=l+2*(r-l)/3; int fx=f(x),fy=f(y); if(fx!=inf&&fx<=fy)r=y; else l=x; } return 0; } |
默默的问一句 能用这个来做餐巾问题吗
求证明凸函数