sad story:我们自己oj的数据貌似有点问题。标程WA了5%
题解:
首先引一下VFK神犇的证明来证明一下这道题是三分。。
{
我来告诉你世界的真相 = =
因为这题能最小费用最大流
每次最短路长度不降
所以是单峰的
最短路长度就是差分值。。
所以一阶导不降。。
是不是简单粗暴
你要证函数是单峰的。
当然是证斜率什么的
}
三分完初始买了多少个玩具,然后就是贪心。
首先我想说这个贪心真动规。虽然它真的是贪心。
首先先说一种错误的贪心。
就是从前往后扫,优先用快的洗,满足后面时间节点的玩具需求。
然后可以发现
A B 2 3
2 3 a b
快速洗衣店1天
慢的洗衣店3天,
就可以卡掉。很轻松。。。好吧,总之这种贪心是错的。
附此错误代码:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 60 #define M 101000 #define inf 0x3f3f3f3f using namespace std; int need[M]; int n,d1,d2,c1,c2,m; int l1,l2; int nd[M],rnd[M]; void init(int mid) { int i,j,k; for(i=1;i<=n;i++)nd[i]=rnd[i]=need[i]; for(i=1;i<=n;i++) { if(mid<=nd[i]) { nd[i]-=mid; return ; } else mid-=nd[i],nd[i]=0; } return ; } long long check(int mid) { long long ret=0; int i,j,k; init(mid); l1=1+d1,l2=1+d2; for(i=1;i<n;i++) { while(l2<=n) { if(rnd[i]<=nd[l2]) { nd[l2]-=rnd[i]; ret+=rnd[i]*c2; break; } else { ret+=nd[l2]*c2; rnd[i]-=nd[l2]; nd[l2++]=0; } } if(rnd[i])while(l1<=n) { if(rnd[i]<=nd[l1]) { nd[l1]-=rnd[i]; ret+=rnd[i]*c1; rnd[i]=0; break; } else { ret+=nd[l1]*c1; rnd[i]-=nd[l1]; nd[l1++]=0; } } } for(i=1;i<=n;i++)ret+=nd[i]*(c1-c2); return ret+mid*m; } int main() { freopen("test.in","r",stdin); int i,j,k; int l=0,r=1,mid,midmid,temp=0; scanf("%d%d%d%d%d%d",&n,&d1,&d2,&c1,&c2,&m); for(i=1;i<=n;i++)scanf("%d",&need[i]),r+=need[i]; if(d1>d2)swap(d1,d2),swap(c1,c2);if(c1<c2)c2=c1; for(i=1;i<d1;i++)temp+=need[i]; for(i=d1;i<=n;i++) { temp+=need[i]; temp-=need[i-d1]; l=max(l,temp); } long long ans=inf; while(1) { if(r-l<=2) { for(i=l;i<r;i++)ans=min(ans,check(i)); break; } mid=l+(r-l)/3,midmid=l+2*(r-l)/3; long long reta=check(mid); long long retb=check(midmid); if(reta<retb)r=midmid; else l=mid; } cout<<ans<<endl; return 0; }
然后就有了正确的思想(标程的思想):
同样很简单、
就是维护几个队列神马的。
优先采用快的洗衣店,然后扫之前采用过的快的洗衣店的次数以及时间
发现有一些可以选择慢的洗衣店,然后当前选择快的洗衣店来代替。
这样进行答案的修改。
最后可以return一个当前玩具数量的函数值。
最后三分求个单峰就好了。
附个usaco的标程。
#include <stdio.h> #include <stdlib.h> #include <algorithm> using namespace std; #define MAX (100005) #define INF (1000000000) int T[MAX]; int queue[MAX], num[MAX]; int sn,sm,so,en,em,eo; int D,N1,N2,C1,C2,Tc; inline void add_new(int x,int q){ queue[en]=x; num[en++]=q; } int f(int t){ sn=sm=so=en=em=eo=0; int r = (Tc-C2)*t; add_new(-200000,t); for(int d=0;d<D;d++){ while(sn!=en && d-queue[sn] >= N1) { num[em]=num[sn]; queue[em++] = queue[sn++]; } while(sm!=em && d-queue[sm] >= N2) { num[eo]=num[sm]; queue[eo++] = queue[sm++]; } int i = T[d]; while(i > 0){ if(so!=eo){ if(num[eo-1] > i){ r += C2*i; num[eo-1]-=i; break; } else { r += C2*num[eo-1]; i -= num[eo-1]; eo--; } } else if(sm!=em){ if(num[em-1] > i){ r += C1*i; num[em-1]-=i; break; } else { r += C1*num[em-1]; i -= num[em-1]; em--; } } else return INF; } add_new(d,T[d]); } return r; } int ternary_search(int s,int e){ while(1){ if(e-s <= 5){ int m = f(s); for(int i=s+1;i<e;i++) m = min(m,f(i)); return m; } int x = s+(e-s)/3, y = s+2*(e-s)/3; int a = f(x); if(a!= INF && a <= f(y)) e=y; else s=x; } } int main(){ scanf("%d%d%d%d%d%d",&D,&N1,&N2,&C1,&C2,&Tc); if(N1 > N2){ N1 ^= N2; N2 ^= N1; N1 ^= N2; C1 ^= C2; C2 ^= C1; C1 ^= C2; } if(C1 < C2){ C2 = C1; } int tsum = 0; for(int i=0;i<D;i++) {scanf("%d",&T[i]); tsum += T[i]; } printf("%d\n",ternary_search(0,tsum+1)); return 0; }
#include <stdio.h> #include <stdlib.h> #include <algorithm> using namespace std; #define MAX (100005) #define INF (1000000000) int T[MAX]; int queue[MAX], num[MAX]; int sn,sm,so,en,em,eo; /* These variables control the queue and point to the start * and end of new, middle, and old toys, respectively. * New toys are ones washed less than N1 days ago, old toys are * washed at least N2 days ago, and middle toys are all the rest. * We essentially keep three queues in a single array, but the manner * in which we add/remove elements guarantees that we never have to * worry about memory overlap. */ int D,N1,N2,C1,C2,Tc; inline void add_new(int x,int q){ queue[en]=x; num[en++]=q; } void flush_new(int x){ while(sn!=en && x-queue[sn] >= N1) { num[em]=num[sn]; queue[em++] = queue[sn++]; } } void flush_mid(int x){ while(sm!=em && x-queue[sm] >= N2) { num[eo]=num[sm]; queue[eo++] = queue[sm++]; } } int f(int t){ //find the minimum cost given that we use t toys sn=sm=so=en=em=eo=0; int r = (Tc-C2)*t; /* * In the following algorithm, we pay even to wash the initial toys, so * we have to subtract this out of our initial cost estimate for * purchasing the toys. This is valid as long as we use all of the toys * we purchase (which is why we start the ternary search at tsum+1 * instead of 5000001). */ add_new(-200000,t); for(int d=0;d<D;d++){ flush_new(d); flush_mid(d); //move any toys toys whose status changes //from being new to middle or middle to old to the appropriate queue int i = T[d]; while(i > 0){ //we deal with the toys in batches to make //the runtime of this function O(N) instead of O(sum ti) if(so!=eo){ //if there are any old toys if(num[eo-1] > i){ //if this batch has more toys than we need, we can stop here r += C2*i; num[eo-1]-=i; break; } else { //otherwise, use all toys in this batch r += C2*num[eo-1]; i -= num[eo-1]; eo--; } } else if(sm!=em){ //else if there are any middle toys if(num[em-1] > i){ r += C1*i; num[em-1]-=i; break; } else { r += C1*num[em-1]; i -= num[em-1]; em--; } } else return INF; //if there are no available toys, //we can't find a solution with this many toys } add_new(d,T[d]); //put the toys we used today back into the queue of toys } return r; } int ternary_search(int s,int e){ while(1){ if(e-s <= 2){ //when e-s is small enough that our ternary search //can get stuck, just handle the end manually int m = f(s); for(int i=s+1;i<e;i++) m = min(m,f(i)); return m; } int x = s+(e-s)/3, y = s+2*(e-s)/3; //sample values 1/3 and 2/3 //of the way through the remaining interval int a = f(x); //f(x) = -1 if x was too few toys to have enough toys each day if(a!= INF && a <= f(y)) e=y; else s=x; } } int main(){ FILE *fin = fopen("toy.in","r"); FILE *fout = fopen("toy.out","w"); fscanf(fin,"%d%d%d%d%d%d",&D,&N1,&N2,&C1,&C2,&Tc); if(N1 > N2){ //set N2 to be greater than N1 N1 ^= N2; N2 ^= N1; N1 ^= N2; C1 ^= C2; C2 ^= C1; C1 ^= C2; } if(C1 < C2){ //if faster way is cheaper C2 = C1; //then set its cost to that of the slower way, //since we could always just use the slower way instead } int tsum = 0; for(int i=0;i<D;i++) { fscanf(fin,"%d",&T[i]); tsum += T[i]; } fprintf(fout,"%d\n",ternary_search(0,tsum+1)); fclose(fin); fclose(fout); return 0; }
最后是该网址
http://cerberus.delosent.com:794/TESTDATA/NOV08.toy.htm