user-0 提问: 大神这个循环看不懂，求解释。http://blog.csdn.net/chenguolinblog/article/details/8991408

回复:
user: user-1
content: 千句解释不如一句例子啊！

user: user-2
content: /* Problem Description 电子科大本部食堂的饭卡有一种很诡异的设计，即在购买之前判断余额。如果购买一个商品之前，卡上的剩余金额大于或等于5元，就一定可以购买成功（即使购买后卡上余额为负），否则无法购买（即使金额足够）。所以大家都希望尽量使卡上的余额最少。 某天，食堂中有n种菜出售，每种菜可购买一次。已知每种菜的价格以及卡上的余额，问最少可使卡上的余额为多少。    Input 多组数据。对于每组数据： 第一行为正整数n，表示菜的数量。n<=1000。 第二行包括n个正整数，表示每种菜的价格。价格不超过50。 第三行包括一个正整数m，表示卡上的余额。m<=1000。  n=0表示数据结束。    Output 对于每组输入,输出一行,包含一个整数，表示卡上可能的最小余额。 */ #include "stdafx.h" #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std;   const int N = 1010; const int MAXN = N*50;   int n , m , sum; int v[N] , dp[MAXN];   int solve(){     memset(dp , 0 , sizeof(dp));						//初始化dp数组，全部赋值为0     sort(v+1 , v+1+n);									//对数组v中的v[1]~v[n]进行排序，也就是对main函数中输入的n个数字进行排序 	for(int i = 1 ; i <= n ; i++)						//输出排序后的v[1]~v[n] 		printf("%d ",v[i]); 	printf("\n");     sum = m-5;											//以下几行需要结合题目以及解题思路理解 														//本题的解题思路是：留最贵的菜最后买，然后选择性购买其它的菜，使饭卡剩余钱数最接近5 														//所以这里的sum就是购买其它菜可以使用的钱数 														// 														// 														//     for(i = 1 ; i < n ; i++){							//从最便宜的菜开始遍历 		printf("i=%d\n",i);								//输出i，也就是第i便宜的菜         for(int j = sum ; j >= v[i] ; j--){				//v[i]就是第i便宜的菜的价格，这个循环从sum开始递减到v[i]，将j称之为剩余的钱数             dp[j] = __max(dp[j] , dp[j-v[i]]+v[i]);		//为dp数组赋值，这里算是算法的关键 														//dp[j]存储的值实际上就是当饭卡里还有j+5的钱数时，j元钱可以在前n-1个菜中消费的最大值 														//通过两层for循环实现对dp数组的赋值 														//举个例子：4种菜，价格分别2、3、5、7，饭卡剩余12元 														/*本程序的输出结果为： 															4 															2 3 5 7 															2 3 5 7 															12 															2 3 5 7 															i=1 															dp[7]=2 dp[6]=2 dp[5]=2 dp[4]=2 dp[3]=2 dp[2]=2 															0 0 2 2 2 2 2 2 															i=2 															dp[7]=5 dp[6]=5 dp[5]=5 dp[4]=3 dp[3]=3 															0 0 2 3 3 5 5 5 															i=3 															dp[7]=7 dp[6]=5 dp[5]=5 															0 0 2 3 3 5 5 7 															-2 														*/ 														//从i=1开始看，v[1]=2，饭卡里有12元，减去5，还有7元可用 														//进入内层for循环，j从7到2，对应的输出dp[7]~dp[2]的值 														//因为只有一个菜，而且一个菜只能买一次，所以dp[7]~dp[2]的值均为这个菜的价格，即v[1] 														//内层循环结束后，外层循环i++，即i=2,v[2]=3,这时表示有两个菜，而且第二个菜的价格比第一个高 														//再次进入内层循环,j从7到3,这时dp[7]~dp[3]会被重赋值 														//首先是dp[7],它的取值是当前dp[7]，也就是2，与dp[7-v[2]]+v[2]的最大值 														//dp[j-v[i]]表示先消费当前最贵的菜v[i]后,剩余的钱能花掉的最大值 														//结合例子来说，就是先花掉v[i]=3后，在前i-1个菜中还能花掉的最大值 														//这里i=2，也就是说7元，花掉3元后，还可以买第一个菜花掉2元，所以dp[7]=5 														//此处可能不理解我们明明有7元，为什么还要计算dp[6], 														//dp[6]~dp[2]都是为后面的i=3、4时计算dp[7]服务的 														//__max(dp[j] , dp[j-v[i]]+v[i])表示剩余j元钱可以花掉的最大值 														//在这两层循环过程中，dp[7]是会变化的，直到循环结束，得出剩余sum=7元时，可以花掉的最大值 			printf("dp[%d]=%d ",j,dp[j]); 		}  		printf("\n");	 		for(int k=0;k<=sum;k++) 			printf("%d ",dp[k]); 		printf("\n");     }     return m-dp[sum]-v[n];								//m是指饭卡里的钱，减去dp[sum],通过上面的计算分析可知(m-dp[sum])>5 														//(m-dp[sum])再减去最贵的菜v[n]，就是饭卡里可以剩余的最小值。 }   int main(){     while(scanf("%d" , &n) && n){					//输入一个数字n，n为0则循环终止，结合题目，n就是饭菜的种类          for(int i = 1 ; i <= n ; i++)				//循环输入n个数字，存储到数组v[1]~v[n],即n种饭菜的价格，这里没有使用v[0]              scanf("%d" , &v[i]); 		 for(i = 1 ; i <= n ; i++)					//打印输入的n个数字 			 printf("%d ",v[i]); 		 printf("\n");          scanf("%d" , &m);							//输入一个数字，结合题目，就是饭卡里剩余的钱          printf("%d\n" , m < 5 ? m : solve());		//如果饭卡里的钱m小于5，不能买任何东西，直接输出m；否则执行solve函数，并打印solve的返回值，这是一个条件表达式     }     return 0; }

user: user-2
content: 引用 4 楼 zhao4zhong1 的回复:千句解释不如一句例子啊！ 很受伤，竟然没给分。

user: user-0
content: 引用 1 楼 lovesmiles 的回复:这是背包问题的 解法。 那个双重循环怎么解释？

user: user-3
content: 这是背包问题的 解法。