Chapter 32: 动态规划小测总结 (USACO的???

前言+总结

四道题两个小时,都是2~3星的DP。最终300分,诶DP弱到不行的我也算是这样了吧orz。

花了快一个小时搞第一题(发现自己好搞笑啊,一直觉得自己的方程很简单会不会漏了什么一直在看= =);然后跳了第二题去打第三题,用了大概十五分钟,感觉这个细节蛮多的,打得我好虚啊;最后一题也大概打了十五分钟吧,挺水的,,一开始还想弄个一维的算了,发现不行= =,就直接改成滚动了√然后重新搞了几下这三题,静态查错了一下,好像没什么不对的,就剩十分钟了,ORZ,没时间搞第二题了QwQ【还想拼拼手速十分钟第二题来着完了发现方法不对啊  果然都要先思考啊。

怎么说,看完题有个大概思路还是很重要的,有时候就不要想那么多那些有的没的= =简直浪费时间。

插曲:

刚打完第一题的我,发现hyc一脸无奈(???)的看着我,我就冲她摇摇头表示我不会做;蒟蒻打完第三题的时候,发现她又看着我,我表示疑惑,于是她说:我刚刚打了1、4题的对拍,发现我第1题sabi了。。。我:???你打完了?!!! hyc:是啊还打了两个对拍。我:ORZORZORZ...too naive的我


题目+题解

第一题


因为只有两个苹果树,且知道了起始位置,那么当确定了移动了多少次的时候就能确定此时处在那一棵树下。

于是设f[i][j],表示i时刻时已经移动了j次。

转移方程为:f[i][j]=max(f[i-1][j],f[i-1][j-1])+(该状态下有没有苹果接)


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
typedef long long LL;
using namespace std;
#define N 1100
#define M 50

int f[N][M],a[N];
int mymin(int x,int y){return (x<y)?x:y;}
int mymax(int x,int y){return (x>y)?x:y;}
int main()
{
	//freopen("bcatch.in","r",stdin);
	//freopen("bcatch.out","w",stdout);
	int n,k,i,j,ans=0;
	memset(f,0,sizeof(f));
	scanf("%d%d",&n,&k);
	for (i=1;i<=n;i++) scanf("%d",&a[i]);
	for (i=1;i<=n;i++)
	{
		f[i][0]=f[i-1][0]+(a[i]==1);
		for (j=1;j<=k;j++)
		{
			if (j>i) break;
			f[i][j]=mymax(f[i-1][j],f[i-1][j-1]);
		 	if (a[i]%2==(j+1)%2) f[i][j]+=1;//判断此状态有没有苹果接
			ans=mymax(ans,f[i][j]);
		}
	}printf("%d\n",mymax(ans,f[n][0]));//本来没有把一直不动的更新到ans里= =数据弱啊
	return 0;
}

============================================

第二题


我跳了。。因为两种东西感觉好麻烦= =

好吧说解法。当你发现能力值只有100的时候,就很容易想到把 什么时候都能去滑 的那个给预处理了。

设bs[i],表示当能力值为i时,能去滑的滑雪中一次所耗费的最小时间。

f[i],就表示一定上第i门课时能滑的最大次数。


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define T 100100

struct node
{
	int m,l,a;
}cl[T];int bs[110];
int f[T];
bool cmp(node x,node y)
{
	if (x.m!=y.m) return x.m<y.m;
	if (x.a!=y.a) return x.a-y.a;
	return x.l<y.l;
}
int mymin(int x,int y){return (x<y)?x:y;}
int mymax(int x,int y){return (x>y)?x:y;}
int main()
{
	//freopen("ski.in","r",stdin);
	//freopen("ski.out","w",stdout);
	int t,s,n,i,j,x,y,ans=0;
	scanf("%d%d%d",&t,&s,&n);
	for (i=1;i<=s;i++)
	 scanf("%d%d%d",&cl[i].m,&cl[i].l,&cl[i].a);
	memset(bs,63,sizeof(bs));
	memset(f,0,sizeof(f));
	for (i=1;i<=n;i++)
	{
		scanf("%d%d",&x,&y);
		bs[x]=mymin(bs[x],y);
	}//预处理bs[]
	for (i=2;i<=100;i++) bs[i]=mymin(bs[i-1],bs[i]);
	sort(cl+1,cl+1+s,cmp);//对课程排下序
	cl[0].m=cl[0].l;cl[0].a=1;
	for (i=1;i<=s;i++)
	  if (cl[i].m+cl[i].l<=t)
	  //看看上完这门课程是否还在限制内
	  {
		  for (j=0;j<i;j++)//枚举上一次上的课是那个
		  {
			  int sc=(cl[i].m-(cl[j].m+cl[j].l))/bs[cl[j].a];
			  //sc就是上次上完课后到这次上课前去滑雪能滑的(最大)次数
			  f[i]=mymax(f[i],f[j]+sc);
		  }
		  ans=mymax(ans,f[i]);
		  ans=mymax(ans,f[i]+(t-cl[i].m-cl[i].l)/bs[cl[i].a]);
	  }
	ans=mymax(ans,t/bs[1]);//这是不上课的
	printf("%d\n",ans);
	return 0;
}

===========================================

第三题


这道题,额贪心水了过去。因为过程中发现自己漏了一些细节,打得好虚!还好都有考虑到。

先按位置排序,去重(某点多个限制就去最小)。[然而似乎数据并不需要我做这些- -

我是直接贪在两个限制间我能到的最大速度嘛,所以首先要保证,当我刚好处于前面位置的限制速度时,我一定能保证后面不会超速。所以就从后往前扫一遍,用后面的限速来限制前面的。以此来保证肯定合法。

处理完这些,就直接扫一遍啊,两个限制点间的速度肯定呈 先递增后递减 或 单调递增/减,而这些取决于这两个点限制速度的大小关系。【不造怎么说。。看代码吧orzorzorz


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
#define maxn 101000

LL mymin(LL x,LL y){return (x<y)?x:y;}
LL mymax(LL x,LL y){return (x>y)?x:y;}
struct node {LL w,v;}a[maxn];
bool cmp(node x,node y)
{
	if (x.w!=y.w) return x.w<y.w;
	return x.v<y.v;
}
int main()
{
	//freopen("bobsled.in","r",stdin);
	//freopen("bobsled.out","w",stdout);
	LL l,n,i,ln,ans,v,w;
	scanf("%I64d%I64d",&l,&n);
	for (i=1;i<=n;i++)
	 scanf("%I64d%I64d",&a[i].w,&a[i].v);
	sort(a+1,a+1+n,cmp);ln=0;
	for (i=1;i<=n;i++)
	 if (a[i].w!=a[i-1].w) a[++ln]=a[i];//去重
	n=ln;ans=0;v=1;w=0;
	for (i=n-1;i>=1;i--)//保证合法性
	 a[i].v=mymin(a[i].v,a[i+1].w-a[i].w+a[i+1].v);
	for (i=1;i<=n;i++)
	{
		if (a[i].v>=v)
		{
			if (a[i].v-v<=a[i].w-w)//速度曲线呈先递增后递减
			{
				ans=mymax(ans,a[i].v+((a[i].w-w)-(a[i].v-v))/2);
				v=a[i].v;
			}else {ans=mymax(ans,v+(a[i].w-w));v+=(a[i].w-w);}
			//单调递增 注意,此时速度不一定能到达限制速度所以不能直接v=a[i].v
		}else
		{
			ans=mymax(ans,v+((a[i].w-w)-(v-a[i].v))/2);//单调递减
			v=a[i].v;
		}w=a[i].w;
	}ans=mymax(ans,v+(l-w));//不要忘了能一直加速至终点!
	printf("%I64d\n",ans);
	return 0;
}

=============================================

第四题


这个很好想啊,f[i]就表示余数为i的有多少个。

数据范围明显让你O(nm)啊,就这样搞搞就好了。

不要作死想一维过了,还是好好打二维吧


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
typedef long long LL;
using namespace std;
#define maxn 2100
#define mod 100000000

LL f[2][maxn];int a[maxn];
int main()
{
	//freopen("fristeam.in","r",stdin);
	//freopen("fristeam.out","w",stdout);
	int n,m,i,j,t;
	scanf("%d%d",&n,&m);
	for (i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		a[i]=a[i]%m;
	}t=1;
	memset(f,0,sizeof(f));
	for (i=1;i<=n;i++)
	{
		for (j=m-1;j>=0;j--)
	 	{
			f[t][(j+a[i])%m]+=f[1-t][j];
			f[t][(j+a[i])%m]=f[t][(j+a[i])%m]%mod;
	 	}f[t][a[i]]++;f[t][a[i]]%=mod;
		for (j=0;j<=m-1;j++)
		{
			f[t][j]=(f[t][j]+f[1-t][j])%mod;
			f[1-t][j]=0;
		}t=1-t;
	 }
	printf("%I64d\n",f[1-t][0]);
	return 0;
}


posted @ 2016-09-28 20:40  OxQ  阅读(118)  评论(0编辑  收藏  举报