【BZOJ1229】【USACO 2008 Nov Gold】 4.Toys sadstory 三分+贪心

sad story:我们自己oj的数据貌似有点问题。标程WA了5%

题解:

复制去Google翻译翻译结果

首先引一下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
时间: 2024-11-17 17:32:30

【BZOJ1229】【USACO 2008 Nov Gold】 4.Toys sadstory 三分+贪心的相关文章

【USACO 2008 Nov Gold】 1.Mixed Up Cows 状压DP、

题解: 首先考虑dfs,但是看到答案的"64bit"就放弃了吧. 所以肯定是组合数.状压DP什么的,尤其是那个16,标准的状压数. 好吧,就是状压DP. f[i][j]表示i是状压的01串表示哪个取了哪个没取,然后j是结尾字符, 虽然水,但是时间复杂度是可以过的. 代码: #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include &

【USACO 2008 Nov Gold】 2.Cheering up the Cows 最小生成树、

题意:可以理解为给你一个图,让你建一个树,然后从某根开始走,每走一条边就要消耗两边点权+边的边权.最后再加上一个根的点权,问最少花多少代价. 题解:改变边权然后做最小生成树. #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 101000 #define inf 0x3f3f3f3f us

【USACO 2008 Nov Gold】 3.Light Switching(lites 开关灯) 区间修改线段树

题意: n.m,n个灯,m次操作 两种操作 0: 这段区间全部状态取反,初始全部为0 1: 询问这段区间有几个灯是亮的. 裸线段树,弱爆了. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 101000 #define inf 0x3f3f3f3f using namespace std; struct Segment_Tree {

【USACO 2008 Mar Gold】 3.Pearl Pairing 贪心 pq

题意:有若干个颜色,每个颜色有若干头牛. 现在将牛进行配对,使得每对颜色都不一样,有SPJ. 题解:一旦某种颜色的牛数量占当前未配对牛总数最多,那么就要群起而攻之! 利用pq或者heap解决. 代码: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 101000 #define inf

【POJ3659】【USACO 2008 Jan Gold】 3.Cell Phone Network 树上最小支配集/贪心 两种做法

题意:求树上最小支配集 最小支配集:点集,即每个点可以"支配"到相邻点,求最少点数可以使所有点被支配. 图上的最小支配集是NP的,但是树上的可以DP做,是O(n)的. 暴力做就好了, f[i]表示此 点被选时的子树全支配的最小代价 g[i]表示其父亲节 点被选时的子树全支配的最小代价 h[i]表示其某子节 点被选时的子树全支配的最小代价 然后暴力转移. (v是子节点) f[x]=∑(min(f[v],min(g[v],h[v])))+1; g[x]=∑(min(f[v],h[v]));

【POJ3666】【USACO 2008 Feb Gold】 2.Cow Game 动规

题意:有若干个数,然后可以花费i的代价让某个数+i或者-i. 现在要求让你把序列排成不升或者不降,问最小代价. 题解: 首先可以证明,最优花费下最后所有的数都可以是现在的某个数: 证:如果两个数调整后在两个数中间,那么可以把两个数都变为其中一个数,而代价显然是等同的. 这个出来后就好做了. 我们可以先离散化一下,然后f[i][j]表示第i个数变为j时1~i这些数保持非严格单调的最小花费 转移时f[i][j]不必n*n枚举,可以维护一个前缀最优(非常水),然后O(1)转移. 然后做一遍非升,一遍非

【BZOJ1589】【USACO 2008 Dec Gold】 1.Trick or Treat on the Farm 基环树裸DP、

没测样例一遍A这真是-- 题意:每个点都有且仅有一个出边(可以出现自环),然后这样一个点出发就会走过且一定走过某些点. 问每个点出发都会走过几个点. 首先这是基环树无疑. 然后就是裸DP了. 这个的关键就是找环,仅此. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 101000 using namespace std; int n

【POJ3657】【USACO 2008 Jan Gold】 1.Haybale Guessing 二分答案,并查集check

题意: 输入n.m表示数列长度为n,有m条有序的限制{l,r,x}. 限制:l~r间所有数最小值为x. 问到第几条限制开始出现矛盾,都不出现输出"0". 题解: 首先这题比较厉害,正常解有点难,不妨转化成二分答案. 我们二分"答案",也就是第ans条出现矛盾. 考虑到若一条限制S所在区间被另一个限制Seg包含,且Seg这条限制的x又比S.x大, 那么也就是意为 ① [Seg.l,Seg.r]间最小值为Seg.x ② [S  .l,S  .r]间最小值为S  .x ③

【POJ3658】【USACO 2008 Jan Gold】 2.Artificial Lake人工湖 单调栈

人工湖 Time Limit: 1 Sec  Memory Limit: 128 MB Description 夏日那让人喘不过气的酷热将奶牛们的烦躁情绪推到了最高点.最终,FJ 决定建一个人工湖供奶牛消暑之用.为了使湖看起来更加真实,FJ决定将湖的 横截面建成N(1 <= N <= 100,000)个连续的平台高低错落的组合状,所有的平台 从左到右按1..N依次编号.当然咯,在湖中注入水后,这些平台都将被淹没. 平台i在设计图上用它的宽度W_i(1 <= W_i <= 1,000