[Noip 2009 普及组 T4] [Luogu P1070] 道路游戏

Posted Sweetness

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Noip 2009 普及组 T4] [Luogu P1070] 道路游戏相关的知识,希望对你有一定的参考价值。

一道很迷的\\(dp\\)

感谢大爱无疆的sy

开始的时候采用的二维状态但是并不对)

然后把状态改成一维\\(AC\\)

\\(Solution\\)

状态:开始想的是\\(f[i][j]\\),表示到达\\(i\\)时间时在\\(j\\)点处的最多金币数。

转移方程设置为:\\(f[i][j]=max\\{f[i-1][j-k]+sum-val[j-k]\\}\\)其中\\(sum\\)表示从\\(j-k\\)走到\\(j\\)所赚的金币数

但这样是不对的,因为题目中:

也就是上一个机器人消失的地方并不一定要买下一个机器人,而是可以换另一个地方买。

如果硬要设计地点的话,应该将\\(f[i-1][j-k]\\)变为\\(max\\{f[i-1][1,2,……n]\\}\\)

这样时间复杂度是\\(O(n^4)\\)的,想过题可能在想\\(peach\\)

经过思考,我们发现地点是没什么用的,所以将第二维去掉,\\(f[i]\\)表示时间为\\(i\\)时能获得的最大金币数;

转移方程:\\(f[i]=max\\{f[i-k]+sum-val[d],d=(j-k)\\%n+n\\}\\)

表示在\\(d\\)点购买的机器人从\\(d\\)点走到\\(j\\)点赚取的金币数,枚举\\(j、d\\),取得\\(f[i]\\)时的最大值

\\(sum\\)表示从\\(d\\)走到\\(j\\)获得的金币数,最简单的方法,暴力求解:

int pay(int a,int b,int x,int y) {
	int rtn=0,j=a;
	for(int i=x+1;i<=y;i++) {
		rtn+=cost[j][i];
		j++;
		if(j>n) j=1;
	}
	return rtn;
}
//表示从a走到b,时间从x变成y所获得的金币数

\\(Code\\):

#include<bits/stdc++.h>

using namespace std;

inline int read() {
	int ans=0;
	char last=\' \',ch=getchar();
	while(ch>\'9\'||ch<\'0\') last=ch,ch=getchar();
	while(ch>=\'0\'&&ch<=\'9\') ans=(ans<<1)+(ans<<3)+ch-\'0\',ch=getchar();
	if(last==\'-\') ans=-ans;
	return ans;
}

int n,m,p; 
int cost[1010][1010];
int val[1010];
int f[1010];

int pay(int a,int b,int x,int y) {
	int rtn=0,j=a;
	for(int i=x+1;i<=y;i++) {
		rtn+=cost[j][i];
		j++;
		if(j>n) j=1;
	}
	return rtn;
}

int main() {
	n=read();
	m=read();
	p=read();
	for(int i=1;i<=n;++i) 
		for(int j=1;j<=m;++j) 
			cost[i][j]=read(); 
	for(int i=1;i<=n;++i) 
		val[i]=read();
	memset(f,0x9f,sizeof(f));
	f[0]=0;
	for(int i=1;i<=m;++i) { // time i
		for(int j=1;j<=n;++j) { // node j
			for(int k=1;k<=p&&k<=i;++k) { // k
				int d=j-k; 
				if(d<=0) 
					d=d%n+n;
				int sum=pay(d,j,i-k,i);
				f[i]=max(f[i],f[i-k]+sum-val[d]);
			}
		}
	}
	printf("%d",f[m]);
	return 0;
}

这样会\\(TLE\\),求\\(pay\\)实在是太慢了。

因为\\(k\\)是从小到大枚举的,也就是从只从\\(j\\)往后退一步,到往后退\\(p\\)步,赚取的金币数刚好是可以累加的。

所以定义一个\\(sum=0\\)每次枚举\\(k\\)时,\\(sum+=cost[j-k][i-k+1]\\)

#include<bits/stdc++.h>

using namespace std;

inline int read() {
	int ans=0;
	char last=\' \',ch=getchar();
	while(ch>\'9\'||ch<\'0\') last=ch,ch=getchar();
	while(ch>=\'0\'&&ch<=\'9\') ans=(ans<<1)+(ans<<3)+ch-\'0\',ch=getchar();
	if(last==\'-\') ans=-ans;
	return ans;
}

int n,m,p; 
int cost[1010][1010];
int val[1010];
int f[1010];

int main() {
	n=read();
	m=read();
	p=read();
	for(int i=1;i<=n;i++) 
		for(int j=1;j<=m;j++) 
			cost[i][j]=read();
	for(int i=1;i<=n;i++) 
		val[i]=read();
	memset(f,0x9f,sizeof(f));
	f[0]=0;
	for(int i=1;i<=m;i++) { // time i
		for(int j=1;j<=n;j++) { // node j
			int sum=0;
			for(int k=1;k<=p&&k<=i;k++) { // zoule k
				int d=j-k; 
				if(d<=0) 
					d=d%n+n;
				sum+=cost[d][i-k+1];
				f[i]=max(f[i],f[i-k]+sum-val[d]);
			}
		}
	}
	printf("%d",f[m]);
	return 0;
}

以上是关于[Noip 2009 普及组 T4] [Luogu P1070] 道路游戏的主要内容,如果未能解决你的问题,请参考以下文章

车站分级 (2013noip普及组T4)(树形DP)

NOIP2012普及组 (四年后的)解题报告 -SilverN

急求NOIP2009普及组试题

NOIP2017普及组赛后心得

[NOIP2009] 普及组

noip2009 普及组