spoj703 Mobile Service---线性dp+状态简化

题目链接:https://vjudge.net/problem/SPOJ-SERVICE

好题。首先可以考虑设计一个四维状态f[i][x][y][z]表示完成第i个,三个人分别位于x,y,z位置的最小费用,但是这样dp的时间复杂度是O(nL^3)的。注意到完成第i个,一定有一个人的位置在Pi。于是可以只用剩下两人的位置来定义状态:f[i][j][k]表示完成第i个,除了位置在Pi的人以外,其他两人的位置在j和k的最小费用,从阶段i-1到阶段i的状态间,3个人都可以移动到Pi。有如下转移方程:

f[i][j][k]=min(f[i][j][k],f[i-1][j][k]+c(Pi-1,Pi)),阶段i-1中,j和k不动,位置Pi-1的人移到Pi

f[i][Pi-1][k]=min(f[i][Pi-1][k],f[i-1][j][k]+c(j,Pi)),阶段i-1中,在Pi-1位置的人不动,位置j的人移到Pi

f[i][j][Pi-1]=min(f[i][j][Pi-1],f[i-1][j][k]+c(k,Pi)),阶段i-1中,在Pi-1位置的人不动,位置k的人移到Pi

注意状态的合法性,因为不能多个人在同一个位置(见下面代码写状态转移时的很多if)。另外原题空间给的多,不用滚动数组,但是空间如果限制到比如128mb就需要了,但是我滚到连样例都没过去......所以先给出一个不用滚动数组的代码

#include<bits/stdc++.h>
using namespace std;

int c[210][210],p[1010],f[1010][210][210];
int t,n,l,i,j,k;

int main(){
	scanf("%d",&t);
	while (t--){
	  scanf("%d%d",&l,&n);
	  for (i=1;i<=l;i++)
	    for (j=1;j<=l;j++) scanf("%d",&c[i][j]);
	  for (i=1;i<=n;i++) scanf("%d",&p[i]);
	  memset(f,0x3f,sizeof(f)); 
	  f[0][1][2]=0; p[0]=3;
	  for (i=1;i<=n;i++)
	    for (j=1;j<=l;j++)
	      for (k=1;k<=l;k++){
	      	if (j==p[i-1]||k==p[i-1]||j==k) continue; //*
	      	if (j!=p[i]&&k!=p[i]) 
			  f[i][j][k]=min(f[i][j][k],f[i-1][j][k]+c[p[i-1]][p[i]]);
	      	if (k!=p[i]&&p[i-1]!=p[i]) 
			  f[i][p[i-1]][k]=min(f[i][p[i-1]][k],f[i-1][j][k]+c[j][p[i]]);
			if (j!=p[i]&&p[i-1]!=p[i]) 
			  f[i][j][p[i-1]]=min(f[i][j][p[i-1]],f[i-1][j][k]+c[k][p[i]]);
		  }
	  int ans=1e9;
	  for (i=1;i<=l;i++)
	    for (j=1;j<=l;j++) ans=min(ans,f[n][i][j]);
	  printf("%d
",ans);
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/edmunds/p/13583032.html