mobile service

Description
一个公司有三个移动服务员。如果某个地方有一个请求,某个员工必须赶到那个地方去(那个地方没有其他员工),某一时刻只有一个员工能移动。被请求后,他才能移动,不允许在同样的位置出现两个员工。从p到q移动一个员工,需要花费c(p,q)。这个函数没有必要对称,但是c(p,p)=0。公司必须满足所有的请求。目标是最小化公司花费。

Input
第一行有两个整数L,N(3<=L<=200, 1<=N<=1000)。L是位置数 ;N是请求数。每个位置从1到L编号。下L行每行包含L个非负整数。第i+1行的第j个数表示c(i,j) ,并且它小于2000。最后一行包含N个数,是请求列表。一开始三个服务员分别在位置1,2,3。

Output
一个数M,表示最小服务花费。
这道题第一次看打了一个暴力,想骗点分
void dfs(int x,int y,int z,int v,int num)
{
	if (num==m+1)
	 {
	 ans=min(ans,v);
	 return ;
     }
	if (x==point[num])  dfs(point[num],y,z,v+c[x][point[num]],num+1);
    else  if (y==point[num])dfs(x,point[num],z,v+c[y][point[num]],num+1);
    else  if (z==point[num]) dfs(x,y,point[num],v+c[z][point[num]],num+1);
    else
    {
	dfs(point[num],y,z,v+c[x][point[num]],num+1);
	dfs(x,point[num],z,v+c[y][point[num]],num+1);
	dfs(x,y,point[num],v+c[z][point[num]],num+1);
    }
}
但是数据范围虽然不算太大,但是足以卡死暴力,那么仔细分析一下发现这道题动态规划是可行的,但是当时用了四维数组+滚动空间虽然没爆,但是时间O(N*L^3)显然会T
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int i,j,n,m,k,l;
int c[1003][1003],point[1003],ans;
int f[202][202][203][5];
int main()
{
	freopen("service.in","r",stdin);
	freopen("service.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (i=1;i<=n;i++)
	 for (j=1;j<=n;j++)
	 scanf("%d",&c[i][j]);
	for (i=1;i<=m;i++)
	 scanf("%d",&point[i]);
	ans=2000000000;
	for (i=1;i<=n;i++)
	 for (j=1;j<=n;j++)
	  for (k=1;k<=n;k++)
	   f[i][j][k][1]=f[i][j][k][2]=2000000000;
	f[1][2][3][1]=0;
	for (i=1;i<=m;i++)
	 {
	  for (j=1;j<=n;j++)
	  for (k=1;k<=n;k++)
	   for (l=1;l<=n;l++)
	    if (j!=k&&k!=l&&l!=j)
	     {
	     	if (j==point[i]||k==point[i]||l==point[i])  f[j][k][l][2]=min(f[j][k][l][1],f[j][k][l][2]);
	        else
	         {
	         	f[point[i]][k][l][2]=min(f[point[i]][k][l][2],f[j][k][l][1]+c[j][point[i]]);
	            f[j][point[i]][l][2]=min(f[j][point[i]][l][2],f[j][k][l][1]+c[k][point[i]]);
	            f[j][k][point[i]][2]=min(f[j][k][point[i]][2],f[j][k][l][1]+c[l][point[i]]);
	         }
	     }
	  for (j=1;j<=n;j++)
	  for (k=1;k<=n;k++)
	   for (l=1;l<=n;l++)
	    if (j!=k&&k!=l&&l!=j)
	     f[j][k][l][1]=f[j][k][l][2],f[j][k][l][2]=2000000000;
     }
   for (i=1;i<=n;i++)
    for (j=1;j<=n;j++)
     for (k=1;k<=n;k++)
      if (j!=k&&k!=i&&i!=j)
       if (i==point[m]||j==point[m]||k==point[m])
        ans=min(ans,f[i][j][k][1]);
   printf("%d",ans);
}
	
那么动归的思路找到了,可是如何减少一种循环呢?仔细分析发现所有有用的状态中必有一维是当前请求服务的位置,那么根据这个共性就可以减少一维。只需每次记录一下上一个的位置即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int i,j,n,m,k,l;
int c[1003][1003],x,cur,last,ans;
int f[5][202][202];
int main()
{
	freopen("service.in","r",stdin);
	freopen("service.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (i=1;i<=n;i++)
	 for (j=1;j<=n;j++)
	 scanf("%d",&c[i][j]);
	memset(f,127/3,sizeof(f));
	cur=0; last=1;
	f[cur][2][3]=0;
	for (i=1;i<=m;i++)
	 {
	 	scanf("%d",&x);
	 	for (j=1;j<=n;j++)
	 	 for (k=1;k<=n;k++)
	 	  if (f[cur][j][k]>-1)
	 	   {
	 	   	if (j!=x&&k!=x)
	 	   	 {
	 	   	 	if(f[1-cur][j][k]==-1||f[1-cur][j][k]>f[cur][j][k]+c[last][x])
	 	   	 	  f[1-cur][j][k]=f[cur][j][k]+c[last][x];
	 	   	 }
	 	   	if (x!=last&&k!=x)
	 	   	 {
	 	   	 	if (f[1-cur][last][k]==-1||f[1-cur][last][k]>f[cur][j][k]+c[j][x])
	 	   	 	  f[1-cur][last][k]=f[cur][j][k]+c[j][x];
	 	   	 }
	 	   	if (x!=last&&j!=x)
	 	   	 {
	 	   	 	if (f[1-cur][j][last]==-1||f[1-cur][j][last]>f[cur][j][k]+c[k][x])
	 	   	 	 f[1-cur][j][last]=f[cur][j][k]+c[k][x];
	 	   	 }
	 	   	f[cur][j][k]=-1;
	 	   }
	 	cur=1-cur;
	 	last=x;
	 }
	ans=2000000000;
	for (i=1;i<=n;i++)
	 for (j=1;j<=n;j++)
	  if (f[cur][i][j]>-1&&f[cur][i][j]<ans)
	   ans=f[cur][i][j];
	printf("%d",ans);
}
	





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值