P1070 道路游戏

P1070 道路游戏

题解

我们先梳理一下题意:

1.一旦开始游戏就要一直玩到第m秒,也就是一个机器人走完之后必须立刻接上下一个新机器人

2.任意时刻只能有一个机器人在活动

3.一旦一个机器人走完了,你可以在任意一个工厂购买一个机器人,并且自己给他设定行走次数,设定的次数在 $ p $ 以内

Solution 1 dfs 28pt

\(dfs(pos,tim,cost,get,res)\) 表示当前在第 \(pos\) 个工厂,经过了 \(m\) 分钟,一共花费了 \(cost\) 元,一共收集了 \(get\) 元,手中机器人还可以走 \(res\) 步,下面分类讨论:

1.当前机器人还可以继续走,那就收集金币并继续走

2.当前机器人不能继续走了,那就要买一个新的机器人,在任意一个工厂选一个并设置步数

3.\(ans=max(get-cost)\)

\(Code\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<queue>
using namespace std;

typedef long long ll;

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

const int maxn=1005;
int n,m,p;
int c[maxn];
int mon[maxn][maxn];
int ans=0;

void dfs(int pos,int tim,int cost,int get,int res)
{
	if(tim>=m){
		ans=max(ans,get-cost);
		return ;
	};
	if(res){
		dfs(pos%n+1,tim+1,cost,get+mon[pos][tim+1],res-1);
		return ;
	} 
	else{
		for(int i=1;i<=n;i++)
		  for(int j=1;j<=p;j++)
		    dfs(i%n+1,tim+1,cost+c[i],get+mon[i][tim+1],j-1);
	}
}

int main()
{
	n=read();m=read();p=read();
	for(int i=1;i<=n;i++)
	  for(int j=1;j<=m;j++)
	    mon[i][j]=read();
	for(int i=1;i<=n;i++) c[i]=read();

	dfs(0,0,0,0,0);
	
	printf("%d\n",ans);
	return 0;
}

过渡阶段 0pt

我们注意到一旦机器人设定了步数,他就会一直走完这些步数,也就是说设置完步数,你的终点也就知道了,那么过程中可以收集到的金币也就确定了,也就是说 \(dfs\)\(res\) 这一维度就没用了,因为你可以直接就走到步数用完

把每条道路对应时间出现的金币数这个二维数组摆出来,观察可以发现金币的收集是按照对角线收集的,那么可以来一个对角线前缀和

然后还可以发现由于机器人用完之后就可以在任意工厂重新选择机器人,那么 \(pos\) 这一维度也没用了

试图优化然后就挂了

void dfs(int tim,int cost,int get)
{
	if(tim>=m){
		ans=max(ans,get-cost);
		return ;
	};
	for(int i=1;i<=n;i++)
		for(int j=1;j<=p&&tim+j<=m;j++){
		   dfs(tim+j,cost+c[i],get+(sum[i][tim+j]-sum[i][tim]));
		   }
}

Solution 2 DP 100pt

神奇的 \(O(n^3) DP\)

\(f[i]\) 表示第 \(m\) 秒可得到的最大价值

然后还要枚举的维度是工厂 \(j\) ,设定的步数 \(k\)

也就是 \(f[i]\) 表示第 \(m\) 秒在工厂 \(j\) ,此时机器人已经把步数用完了,这种情况下得到的最大价值(已经扣除买机器人的费用),由于第 \(m\) 秒终点是 \(j\) ,我们可以通过枚举设定的步数 \(k\) 来确定起点,扣除相应的花费,得到相应的金币数,枚举步数建议从 \(1->p\) ,因为在枚举设定步数为 \(k\) 的时候,它一定会走过步数为 \(k-1\) 的路径,只是比 \(k-1\) 的路径多了一步向 \(k-1\) 转移的路

\(Code\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<queue>
using namespace std;

typedef long long ll;

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

const int maxn=1005;
int n,m,p;
int c[maxn];
int mon[maxn][maxn];
int ans=0;
int f[maxn];

int main()
{
	
	n=read();m=read();p=read();
	for(int i=1;i<=n;i++)
	  for(int j=1;j<=m;j++){
	  	mon[i][j]=read();
	  } 
	for(int i=1;i<=n;i++) c[i]=read();

    memset(f,-63,sizeof(f));
    f[0]=0;
    for(int i=1;i<=m;i++)
      for(int j=1;j<=n;j++){
      	int pre=(j-1)==0?n:j-1;
      	int sum=mon[pre][i];
      	for(int k=1;k<=p;k++){
	    	if(i-k<0) break;
      	    f[i]=max(f[i],f[i-k]-c[pre]+sum);
      	    pre=(pre-1)==0?n:pre-1;;
      	    sum+=mon[pre][i-k];
	    }
	  }
        
	printf("%d\n",f[m]);
	return 0;
}

posted @ 2020-01-08 14:39  晔子  阅读(373)  评论(0编辑  收藏  举报