hdu1227 Fast Food
这个题目如果直接暴力枚举的话,复杂度是n*C(n,k),最坏情况约10^37。
考察下面这种情况:
(n=5,k=3,粗体及加下划线的节点为depot所在地)
① ② ③ ④ ⑤
① ② ③ ④ ⑤
对于④及以后的节点,在④被选中后,距离它们最短的depot一定不会在④之前(因为距离一定比④远)。
也就是说④及之后节点的最优情况与④之前的节点无关,图示两种情况对于④及以后节点是等价的。
状态基本上就出来了。
我从中提取出的状态是[s][num],表示在s号节点被选定后,节点s……n之间还剩num个位置要选时的最优值。
预处理出disSum[N][N]数组,表示在节点i和节点j被选定后(j>i),i……j之间的节点的取值(跟着i还是j)之和。
那么,
dp[s][num]=min( dp[i][num-1] + disSum[s][i] ) i=s+1……n-num+1
复杂度是O(n*n*k)
/*
设计状态的时候,如果[s][num]表示的是节点s……n还剩num个位置要选时的最优值,并不要求节点s已被选定的话,这个dp就不成立了。
[3][1]的最优方案是:③ ④ ⑤
[2][2]的最优方案是(可以构造这样的例子):② ③ ④ ⑤, ③节点距离②节点更近,此时[3][1]的最优值也就改变了,不满足无后效性。
*/
View Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N = 205,K = 35,INF = 0x7fffffff; 6 7 int dp[N][K],n,m; 8 int dis[N],disSum[N][N]; 9 10 int solve(int s,int num) 11 { 12 if( dp[s][num]!=-1 ) return dp[s][num]; 13 int ret=INF; 14 if( !num ) 15 { 16 ret=0; 17 for(int i=s+1;i<=n;i++) 18 ret+=dis[i]-dis[s]; 19 return ret; 20 } 21 for(int i=s+1;i<=n-num+1;i++) 22 ret=min(ret,disSum[s][i]+solve(i,num-1)); 23 return dp[s][num]=ret; 24 } 25 26 int main() 27 { 28 int t=0; 29 while( scanf("%d%d",&n,&m),n||m ) 30 { 31 memset(dp,-1,sizeof(dp)); 32 dis[0]=0; 33 for(int i=1;i<=n;i++) 34 scanf("%d",&dis[i]); 35 for(int i=1;i<n;i++) 36 for(int j=i+1;j<=n;j++) 37 { 38 int sum=0; 39 for(int k=i;k<=j;k++) 40 sum+=min(dis[k]-dis[i],dis[j]-dis[k]); 41 disSum[i][j]=sum; 42 } 43 int ans=INF; 44 for(int i=1;i<=n-m+1;i++) 45 { 46 int sum=0; 47 for(int j=1;j<=i;j++) sum+=dis[i]-dis[j]; 48 ans=min(ans,sum+solve(i,m-1)); 49 } 50 printf("Chain %d\n",++t); 51 printf("Total distance sum = %d\n\n",ans); 52 } 53 return 0; 54 } 55 /* 56 这个稍有些难想,对无后效性再有了理解。 57 */