题目:
问题描述:
每一年,John都会带着他的奶牛们去赶集。集市一共有n(1n400)个摊位,第i个摊位会在特定的时间Pi(0Pi109)对当时在摊位里的顾客送出一份精美的礼物.John当然得到了这个消息,于是他希望能拿到尽量多的礼物送给他的奶牛们。也就是说,他想尽可能多地在某摊位发放礼物的时候,正好呆在这个摊位。
经过一定的调查,John弄清楚了从i号摊位走到j号摊位所需要的时间Ti,j (1Ti,j106)。虽然乡间小路奇特的布局使得从i号摊位走到j号摊位的最短路不一定是直接连接这两个摊位的那条,但John并不会选择那些会经过其他摊位的路线,只是直接走到目标摊位等待礼物的送出.此外,Ti,j并不一定等于Tj,i,由于John爬山的速度总是很慢.
John在时间0时于1号摊位开始他的旅途.请你帮他设计一条路线来获得尽可能多的礼物。
输入文件:
第1行一个正整数n(1n400)个摊位。
接下来n行每行一个整数Pi(0Pi109)。
接下来一个n*n的矩阵描述Ti,j
输出文件:
输出仅一行一个整数,表示John最多能拿到的礼物的个数。
样例输入输出
cfair.in
4 13 9 19 3 0 10 20 3 4 0 11 2 1 15 0 12 5 5 13 0
cfair.out
3
输入输出样例说明
一共有4家摊位。1号摊位会在时间13送出一份礼物,2号摊位送出礼物的时间为9,3号摊位是时间19,4号摊位是时间3。John先在时间3走到4号摊位,正好拿到送出的礼物.然后他再直接走到2号摊位(不经过任何中转摊位),在时间8到那儿,然后等待1单位时间,在时间9拿到摊位送出的礼物后,马上出发去1号摊位,又正好能在时间13到达并拿到第3份礼物。
程序:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int MAX = 400 + 1; 4 int n, g[MAX][MAX], p[MAX], f[MAX]; 5 int main() 6 { 7 freopen("cfair.in","r",stdin); 8 freopen("cfair.out","w",stdout); 9 memset(g,0,sizeof(g)); 10 cin >> n; 11 for (int i = 1; i <= n; i++) 12 { 13 cin >> p[i]; 14 } 15 for (int i = 1; i <= n; i++) 16 for (int j = 1; j <= n; j++) 17 cin >> g[i][j]; 18 for (int i = 1; i <= n; i++) 19 if (g[1][i] <= p[i]) 20 f[i] = 1; 21 bool flag = true; 22 while (flag) 23 { 24 flag = false; 25 for (int i = 1; i <= n; i++) 26 for (int j = 1; j <= n; j++) 27 if (g[i][j] <= (p[j]-p[i]) && f[j] <= f[i] && i != j) 28 { 29 f[j] = f[i] + 1; 30 flag = true; 31 } 32 } 33 int ans = 0; 34 for (int i = 1; i <= n; i++) 35 ans = max(ans, f[i]); 36 cout << ans << endl; 37 return 0; 38 }
分析:
题目不难理解,这道题的数据也不大,用矩阵就足够了。很容易理解,这是一个简单的枚举,或者叫动态规划。其他的无非就是一些套路问题了。
if (g[i][j] <= (p[j]-p[i]) && f[j] <= f[i] && i != j)
程序当中最终要的就是这一行,简单解释一下三个判断的作用。第一个判断意味着从摊位i领完奖品走到j时间足够再领到j摊位的奖品(路程与摊位送礼物时间的差比较)。第二个就是需要更新f[j]的值(当前值更优)。第三个判断是否是i=j,否则自己领完走到自己再领显然是错误的。
很容易注意到,程序当中又一个非常奇怪的while()存在。但仔细想一想为什么就能明白了:因为枚举一次以后可能更新过的值还能更新别的值,这样就要反复循环,直到循环不再改编任何f数组的值为止。(flag==false)