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