做题感悟: 这题因为输出的时候没有看到单复数,wa了老半天,无语,净犯这些低级错误。
解题思路:
(1) . 看这题首先想到区间 dp ,一段区间如果要找一个服务的饭店那是一定的(奇数的时候是中间的那个,偶数的时候中间的两个任意一个),这样我们就可以预处理出来任意一个区间放置一个主饭店服务此区间各个饭店的路程总和,同时记录下左右区间端点值,分别用 le ,rt 代表 ,这样为下面的 dp 做好了铺垫。
动态方程:dp[ rt ] [ k ] = min( dp[ rt ] [ k ] , dp [ le - 1 ] [ k - 1 ] + w ) ; dp[ rt ] [ k ] 代表用 k 个主饭店服务到第 rt 个饭店最小的路程,min 括号中 dp[ rt ] [ k ] 代表不放置当前区间 ,dp[ le - 1 ][ k - 1 ] + w 代表放上当前区间 。
(2) , 跟上面一样先处理出来 d[ i ] [ j ] 代表 i ~ j 这一段区间放置一个主饭店的总路程,那么用 dp [ i ] [ j ] 代表 用了 i 个主饭店管理到第 j 个饭店的最小路程。
动态方程: dp [ i ][ j ] = min( dp[ i ] [ j ] , dp[ i - 1 ] [ k ] + d[ k + 1 ] [ j ] ) ;
代码(1):
#include<iostream>
#include<fstream>
#include<iomanip>
#include<ctime>
#include<fstream>
#include<sstream>
#include<stack>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#include<cstdio>
#include<algorithm>
#define INT long long int
using namespace std ;
const double esp = 0.00000001 ;
const int mod = 1e9 + 7 ;
const int MY = 30 + 10 ;
const int MX = 30000 + 10 ;
int n ,m ,num ,k ;
int dp[MX][MY] ,sum[MX] ,g[MX] ,pre[MX][MY] ;
struct node
{
int x ,le ,rt ,w ;
}T[MX] ;
void DP()
{
int le ,rt ,w ;
memset(dp ,0x3f ,sizeof(dp)) ;
dp[0][0] = 0 ;
for(int i = 0 ;i < num ;++i)
{
le = T[i].le ; rt = T[i].rt ;
w = T[i].w ;
for(int k = 1 ;k <= m ;k++)
if(dp[rt][k] > dp[le-1][k-1] + w)
{
dp[rt][k] = dp[le-1][k-1] + w ;
pre[rt][k] = i ;
}
}
}
void input()
{
num = 0 ;
sum[0] = 0 ;
for(int i = 1 ;i <= n ;i++)
{
scanf("%d" ,&g[i]) ;
sum[i] = sum[i-1] + g[i] ;
}
for(int i = 1 ;i <= n ;i++)
for(int j = i ;j <= n ;j++)
{
T[num].le = i ;
T[num].rt = j ;
int mid = (i + j)/2 ;
T[num].w = g[mid]*(2*mid-i-j)+sum[j]+sum[i-1]-sum[mid]-sum[mid-1] ;
T[num++].x = mid ;
}
}
void output(int x ,int k) // 递归打印
{
int pi ,le ,rt ;
pi = pre[x][k] ;
if(k)
{
le = T[pi].le ;
rt = T[pi].rt ;
output(le-1 ,k-1) ;
if(le != rt) //注意单复数
cout<<"Depot "<<k<<" at restaurant "<<T[pi].x<<" serves restaurants "<<le<<" to "<<rt<<endl ;
else cout<<"Depot "<<k<<" at restaurant "<<T[pi].x<<" serves restaurant "<<T[pi].x<<endl ;
}
return ;
}
int main()
{
int cse = 1 ;
while(scanf("%d%d" ,&n ,&m) ,n+m)
{
input() ;
DP() ;
cout<<"Chain "<<cse++<<endl ;
output(n ,m) ;
cout<<"Total distance sum = "<<dp[n][m]<<endl ;
cout<<endl ;
}
return 0 ;
}
代码(2):
#include<iostream>
#include<fstream>
#include<iomanip>
#include<ctime>
#include<fstream>
#include<sstream>
#include<stack>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#include<cstdio>
#include<algorithm>
#define INT long long int
using namespace std ;
const double esp = 0.00000001 ;
const int mod = 1e9 + 7 ;
const int MY = 30 + 10 ;
const int MX = 200 + 10 ;
int n ,m ,num ,k ;
int dp[MX][MX] ,sum[MX] ,g[MX] ,pre[MX][MX] ,d[MX][MX] ;
void DP()
{
memset(dp ,0x3f ,sizeof(dp)) ;
for(int i = 1 ;i <= n ; ++i)
dp[1][i] = d[1][i] ;
for(int i = 2 ;i <= m ; ++i)
for(int j = i ;j <= n ; ++j)
for(int k = i-1 ;k < j ; ++k)
if(dp[i][j] > dp[i-1][k] + d[k+1][j])
{
dp[i][j] = dp[i-1][k] + d[k+1][j] ;
pre[i][j] = k ;
}
}
void input()
{
num = 0 ;
sum[0] = 0 ;
for(int i = 1 ;i <= n ;i++)
{
scanf("%d" ,&g[i]) ;
sum[i] = sum[i-1] + g[i] ;
}
for(int i = 1 ;i <= n ;i++)
for(int j = i ;j <= n ;j++)
{
int mid = (i + j)/2 ;
d[i][j]= g[mid]*(2*mid-i-j)+sum[j]+sum[i-1]-sum[mid]-sum[mid-1] ;
}
}
void output(int k ,int x) // 递归打印
{
if(k)
{
output(k-1 ,pre[k][x]) ;
cout<<"Depot "<<k<<" at restaurant "<<(pre[k][x]+1+x)/2<<" serves restaurants "<<pre[k][x]+1<<" to "<<x<<endl ;
}
return ;
}
int main()
{
int cse = 1 ;
while(scanf("%d%d" ,&n ,&m) ,n+m)
{
input() ;
DP() ;
cout<<"Chain "<<cse++<<endl ;
output(m ,n) ;
cout<<"Total distance sum = "<<dp[m][n]<<endl ;
cout<<endl ;
}
return 0 ;
}