NEUOJ 1052――Road or Flight(Easy DP)
一,原题及数据
Problem 1052 - Road or Flight
Time Limit:1000MS Memory Limit:65536KB
Total Submit:144 Accepted:25
Description
The king has been out to work for a long time and he wants to go back to his queen as fast as possible. The king is in city 0 and the queen is in city N. There are roads and flights connecting city i to city (i+1) for all i between 0 and N-1, inclusive. Hence the queen has asked the king to take at most K flights on his way. Your should tell the king the minimum amount of time in which the king can reach his queen.
Input
There are several test case, for each case:
First line, contains two integers N,K(0 <= K <= N <= 50) as the Describe.
Follow N lines, each line contains two integers roadtime,flighttime (0 <= roadtime,flighttime <= 1000) representing the time it takes to travel from city i to city (i+1) by road and by flight respectively.
Output
For each test case, you just output minimum amount of time in which the king can reach his queen.
Sample Input
3 1
4 1
6 2
7 3
7 2
50 797
287 661
621 644
266 102
224 114
68 452
636 420
Sample Output
13
1772
二,思路及示例分析
这个题,一上来第一反应就写了一个dfs。抱着侥幸的心理交了一次,很明显的WA了。
int dfs(int v,int m)//dfs
{
if(v==n-1)//reach
{
if(m>=k)return r[v];
return min(r[v],f[v]);
}
if(m>=k)//over k
return road(v);
return min(r[v]+dfs(v+1,m),f[v]+dfs(v+1,m+1));
}
不过这个dfs确实很有意义的。这个dfs给了我很多的启发。先解释一下这个算法的思路。
令r[i],f[i]表示i,i+1之间的费用。
Dfs(v,m)表示到已经乘坐了m次飞机,从v到终点的最小费用。
递归的过程为:
如果v就是终点
如果m>=k,既是坐飞机的次数超过了限制,则返回r[v]。
否则,返回r[v],f[v]的较小值。
如果m>=k,
返回从m到终点选择road方式的总费用。
返回在v点是选择两种不同方式的最小值。如果选择road,则m依然不变,费用可以表示为r[v]+dfs(v+1,m),即当前的费用加上从v+1到终点的费用,飞行次数已经使用了m次。
如果不选择road,则m+1,费用可以表示为f[v]+dfs(v+1,m+1),即当前的费用加上从v+1到终点的费用,飞行次数已经使用了m+1次。
不过这个算法无疑是TLE。但是容易理解,我立即将他改成了DP的形式。令数组dp[i][j]表示已经乘坐了j次飞机,从i到终点的最小费用。则原题的解为dp[0][0]。问题转化为求dp[0][0]。
其中状态转移方程为:
Dp[i][j]=
这样可以用一个二重循环就可以解决了。
三,程序代码
#include<stdio.h>
const int N=51;
int r[N];//r[i] biaoshi i-->i+1
int f[N];
int n,k;
int dp[N][N];//record
int min(int a,int b)
{
return a<b?a:b;
}
int road(int v)
{
int i=v,sum=0;
for(;i<n;i++)
sum+=r[i];
return sum;
}
//dp return a[0][0]
void dfstodp()
{
int i,j;
for(i=n-1,j=0;j<k;j++)
{
dp[i][j]=min(r[i],f[i]);
}//init d[n-1] from d[n-1][0] to d[n-1][k-1];
for(j=k,i=n-1;i>=0;i--)
{
dp[i][j]=road(i);
}//init d[][k] from d[n-1][k] to d[0][k];
for(i=n-2;i>=0;i--)
for(j=k-1;j>=0;j--)
dp[i][j]=min(r[i]+dp[i+1][j],f[i]+dp[i+1][j+1]);
}
int main()
{
int i=0;
int sum=0;
while(scanf("%d%d",&n,&k)!=EOF)
{
for(i=0;i<n;i++)
scanf("%d%d",&r[i],&f[i]);
dfstodp();
printf("%d\n",dp[0][0]);
}
return 0;
}
四,总结
1,DP的难点在于构造状态转移方程。简单的不带状态回退的DFS应该都能用DP来实现吧。
2,能用非递归算法的,最好不要用递归算法。除非时空都相同,深度也差不多。