备战2023蓝桥国赛-移动服务

题目描述:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
解析:
这道题我想复杂了,一开始我是这样想的:设dp[i][j]表示按顺序满足到第i个请求时,最初在j号点的人到达第i个请求的位置的情况下的最小花费,state[i][j]表示按顺序满足到第i个请求时,最初在j号点的人到达第i个请求的位置的情况下的各个人的位置状态,状态转移方程就为dp[i][j]=dp[i-1][k]+g[a][requested_loc[i]],然后就wa了。。。10个过了2.
之后我就一直用这个方法来想,始终过不了,后面不得不看题解。
看完题解后我才发现我一直有个思维误区,那就是必须把原先在1,2,3号点的位置改变要具体表示出来,而且是精确到顺序,正因为这个我一直没有想出正确解法,但其实大可不必如此,题目是求最小值,又不是求到达终点后原先在1,2,3号点各点的精确位置,没必要精确表示原先在1,2,3号点的位置信息,只需大致表示出来就行。
这道题的正确解法是:设f[i][x][y]表示已经处理完前i个请求,且三个服务员分别在p[i], x, y的所有方案的集合,f[i][x][y]的值是集合中所有方案的花费的最小值,那么动态转移方程就为:
位于p[i]的服务员出发前往p[i + 1],此时状态变成f[i + 1][x][y] = f[i][x][y] + w[p[i]][p[i + 1]];
位于x的服务员出发前往p[i + 1],此时状态变成f[i + 1][p[i]][y] = f[i][x][y] + w[x][p[i + 1]];
位于y的服务员出发前往p[i + 1],此时状态变成f[i + 1][x][p[i]] = f[i][x][y] + w[y][p[i + 1]];最终答案从f[m][1…n][1…n]取最小值即可。
错误代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1010,L=210;
int dp[N][5],state[N][5];//dp[i][j]表示按顺序满足到第i个请求时,最初在j号点的人到达第i个请求的位置的情况下的最小花费 
//state[i][j]表示按顺序满足到第i个请求时,最初在j号点的人到达第i个请求的位置的情况下的各个人的位置状态, 
//比如最初在1号点、2号点和3号点的人此时分别在4,5,6号点,那么4*200*200+5*200+6=161006,即state[i][j]=161006。 
int g[L][L]; //存图 
int requested_loc[N];
int n,l;
int main()
{
	scanf("%d%d",&l,&n);
	for(int i=1;i<=l;i++)
	{
		for(int j=1;j<=l;j++)
		scanf("%d",&g[i][j]);
	}
	for(int i=1;i<=n;i++) scanf("%d",&requested_loc[i]);
	
	memset(dp,0x3f,sizeof(dp));
	
	if(requested_loc[1]!=2&&requested_loc[1]!=3)
	{
		dp[1][1]=g[1][requested_loc[1]];
		state[1][1]=201*201*requested_loc[1]+201*2+3;
	}
	
	
	if(requested_loc[1]!=1&&requested_loc[1]!=3)
	{
		dp[1][2]=g[2][requested_loc[1]];
		state[1][2]=201*201*1+201*requested_loc[1]+3;
	}
	
	
	if(requested_loc[1]!=1&&requested_loc[1]!=2)
	{
		dp[1][3]=g[3][requested_loc[1]];
		state[1][3]=201*201*1+201*2+requested_loc[1];
	}
	
	/*
	for(int j=1;j<=3;j++)
	{
		int temp=state[1][j];
		int a=temp/40000,b=temp%40000/200,c=temp%40000%200;
		printf("各点位置:%d %d %d dp[1][%d]的值为:%d\n",a,b,c,j,dp[1][j]); 
	}*/
	
	
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<=3;j++)
		{
			for(int k=1;k<=3;k++)
			{
				int temp=state[i-1][k];
				int a=temp/40401,b=temp%40401/201,c=temp%40401%201;
				//cout<<a<<" "<<b<<" "<<c<<endl;
				if(j==1)
				{
					if(requested_loc[i]!=b&&requested_loc[i]!=c)
					{
						if(dp[i][j]>dp[i-1][k]+g[a][requested_loc[i]])
						{
							dp[i][j]=dp[i-1][k]+g[a][requested_loc[i]];
							state[i][j]=201*201*requested_loc[i]+201*b+c;
						}
					}
					
				}
				else if(j==2) 
				{
					if(requested_loc[i]!=a&&requested_loc[i]!=c)
					{
						if(dp[i][j]>dp[i-1][k]+g[b][requested_loc[i]])
						{
							dp[i][j]=dp[i-1][k]+g[b][requested_loc[i]];
							state[i][j]=201*201*a+201*requested_loc[i]+c;
						}
					}
					
				}
				else {
					if(requested_loc[i]!=a&&requested_loc[i]!=b)
					{
						if(dp[i][j]>dp[i-1][k]+g[c][requested_loc[i]])
						{
							dp[i][j]=dp[i-1][k]+g[c][requested_loc[i]];
							state[i][j]=201*201*a+201*b+requested_loc[i];
						}
					}
					
				}
			}
			/*
			int temp=state[i][j];
			int a=temp/40000,b=temp%40000/200,c=temp%40000%200;
			printf("各点位置:%d %d %d dp[%d][%d]的值为:%d\n",a,b,c,i,j,dp[i][j]); */
		}
	}
	int res=min(dp[n][1],min(dp[n][2],dp[n][3]));
	cout<<res;
	return 0;
}

正确代码:

#include<bits/stdc++.h>
using namespace std;
const int L=210,N=1010,INF=0x3f3f3f3f;
int g[L][L],f[N][L][L],p[N];
int main()
{
	int l,n;
	scanf("%d%d",&l,&n);
	for(int i=1;i<=l;i++)
	{
		for(int j=1;j<=l;j++)
		scanf("%d",&g[i][j]);
	}
	for(int i=1;i<=n;i++) scanf("%d",&p[i]);
	memset(f,0x3f,sizeof(f));
	p[0]=1;
	f[0][2][3]=0;
	for(int i=0;i<n;i++)
	{
		for(int x=1;x<=l;x++)
		{
			for(int y=1;y<=l;y++)
			{
				int z=p[i];
				if(x==y||x==z||y==z) continue;
				int v=f[i][x][y];
				f[i+1][x][y]=min(f[i+1][x][y],v+g[p[i]][p[i+1]]);//p[i]->p[i+1]
				f[i+1][z][y]=min(f[i+1][z][y],v+g[x][p[i+1]]);//x->p[i+1]
				f[i+1][x][z]=min(f[i+1][x][z],v+g[y][p[i+1]]);//y->p[i+1]
			}
		}
	}
	int res=INF;
	for(int x=1;x<=l;x++)
	{
		for(int y=1;y<=l;y++)
		{
			int z=p[n];
			if(x==y||x==z||y==z) continue;
			res=min(res,f[n][x][y]);
		}
	}
	cout<<res;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值