user-0 提问: ACM题目   不知道判断方法是什么？求大大解释解释

回复:
user: user-0
content: 谢谢@孩皮妞野  大神  我终于坐出来了  按照您的思路  还有那个排列组合  嘿嘿   但是不知道那个递归为什么会错  实在是想不明白   还有您提得那个问题  把if放到check()后面就行了  我明白了 谢谢  非常感谢

user: user-0
content: #include<stdio.h> #include<stdlib.h> #include<string.h> int n,m;//一共n元  一共m件物品 struct GOOD { 	int pi,mi,vi; }gd[17]; int MAX(int a,int b) { 	return a>b?a:b; } int makeMAX(int x,int y)//x件物品   剩余y元   递归 { 	int r1,r2;      	if(x==0)return 0; 	if(y>=gd[x].pi*gd[x].mi){ 		r1=0+makeMAX(x-1,y); 		r2=gd[x].mi+makeMAX(x-1,y-gd[x].pi*gd[x].mi); 	} 	else r1=makeMAX(x-1,y); 	return MAX(r1,r2); } int main() { 	int max; 	struct GOOD *p; 	while(scanf("%d%d",&n,&m)!=EOF) 	{ 		for(p=gd+1;p<=gd+m;p++) 		{ 			scanf("%d %d",&p->pi,&p->mi); 			p->vi=p->pi*p->mi; 		} 	    max=makeMAX(m,n); 		printf("%d\n",max); 		 	} 	return 0; }  这个我用递归作的(买或者不买)  我自己想的那些数据都能通过 但是还是WA  每信心了

user: user-0
content: 引用 8 楼 FancyMouse 的回复:bool都不懂就别做acm题了。先搞懂怎样写程序再来搞acm 大一新生刚参加呀  下学期才开始培训  这个是寒假的作业.......

user: user-1
content: bool都不懂就别做acm题了。先搞懂怎样写程序再来搞acm

user: user-2
content: 上面的方法特意跳过了全部不选的组合。你知道怎样加上吗？如果不加，能不能保证仍然可以得到正确的解？

user: user-2
content: 哦，bool就是只能为逻辑值(真或假)的数据类型。现在C也有了，#include <stdbool.h>就好了。  一个函数直接或间接调用自身，就叫递归。递归是一种比较一般化的术语，这里用的回溯法就是一种递归遍历的方法。  语言是来表达思想的，它并不教你如何思想。算法是思想本身。程序=数据结构+算法。  简单的程序可以这样，先把商品信息读入一个数组中，可以不排序。然后用一个同等大小的数组 int picked[count]; 如果picked[i]为0，表示不选这种商品，如果picked[i]为1，表示选取了这个商品。你能够想出办法产生所有选取商品的方案吗？如果你排列组合有一点基础，你应该不难理解这是一个乘法原理问题，第一个商品有选和不选两种可能，第二种也是，....第count种也是，所以总组合数是2x2x....x2=2^count. 如果你对2进制有了一些认识，你会发现这又相当于是count位的二进制数的从0开始自增到重归于0(溢出)能表达的数。  那么你可以这样写程序  // 先把picked数组初始化为0，都为0就是都不选... for(int i=0; i<count; ++i)     picked[i]=0 // 然后开始自增 // 把最低位+1， 如果得到的是2，那就往更高位进位，如果更高位进位后得到2，就往 // 更更高位进位。如果需要进位到count位(这里是按c的惯例从0开始数), 我们就得到了所有的可能 while(1) {     int i=0;     while(i!=count && ++picked[i]==2)// +1或进位后，第i位溢出     {           picked[i]=0; //归0           ++i; //向更高为进位     }     if(i==count) //要往更高位进位了，这时我们已经处理了所有可能的组合，退出循环         break;     // 好了，我们得到了独一无二的一种组合，如果picked[i]位为0，表示小B选中了这个商品，反之     // 表示小B放弃该商品     check(); // 检查这个组合，两个方面，第一，合法性，有没有超支？ 第二，最优性，该组合的              // 的数量是否大于目前最好组合的数量? 如果是当然记住这个更优解的数量 }   check函数留给你自己写。花点功夫理解我上面的解释。好的习惯是努力想，想不明白再问。

user: user-0
content: 汗   好多没学过的东西  bool什么的不懂  是数据结构的莫  我现在才刚刚学完c语言 我问了问我的老师  他说用递归遍历   汗  也不懂 每学过   大神  有简单的程序莫

user: user-2
content: 呕，这个原则上是按单价从低到高。  排序是为了方便贪心选择剪枝。如果不考虑剪枝，无需排序。  0-1背包，和这题都是典型的状态空间是子集树的搜素问题，时间复杂度是O(2^n), 回溯法是系统搜索状态空间是子集树或排序树的极好算法，简单，不易写错--在你掌握了这个方法之后。那个基本是套公式，子集树和排序树各一个公式。你觉得无处下手，估计还不了解这个方法，建议搜索学习之。  多说一下为什么是2^n. 对每一中商品，我可做两种选择，选或者不选，这样n中商品就有2^n次方中可能，我只要把这2^n次方中可能都列出来，去掉不可行的，从剩下的找出有最大值的，这个问题就解决了。因为我枚举了整个状态空间，得到的必然是最优解。但是指数增长是很快的，实际上在搜索的中途，对肯定非法(钱不够了)或者肯定不能产生最优解的分支，都可以直接剪掉。比如我的一组16总商品的测试数据，只需8000多次back_track, 而非65536次...

user: user-2
content: 剪枝效果并不明显。这个问题规模较小，不剪枝也应该可以   #include <stdio.h> #include <algorithm>  //#define _DEBUG  struct item{ 	int price; 	int qty; 	bool operator <(const item& i){ return price<i.price; } };  item items[16]; bool picked[16]; int count; int money; int maxqty;  #ifdef _DEBUG int track_count; #endif   void back_track(int n, int& curqty, int& curbalance); bool greedy_pick();  int main() { 	int curqty, curbalance; 	scanf("%d%d",&money, &count); 	for(int i=0; i<count; ++i) 		scanf("%d%d", &items[i].price, &items[i].qty); 	std::sort(items, items+count); 	maxqty=0; 	curqty=0; 	curbalance=money; 	if(!greedy_pick()) 		back_track(0, curqty, curbalance); #ifdef _DEBUG 	printf("back track called %d times\n", track_count); #endif 	printf("%d\n", maxqty); 	return 0; }   bool viable(int curqty, int curbalance) { 	return true; 	for(int i=0; i<count; ++i) 	{ 		if(!picked[i] && curbalance>items[i].price*items[i].qty) 		{ 			curqty+=items[i].qty; 			curbalance-=items[i].price*items[i].qty; 		}else{ 			curqty+=curbalance/items[i].price; 			break; 		} 	} 	return curqty>maxqty; }  void back_track(int i, int& curqty, int& curbalance) { #ifdef _DEBUG 	++track_count; #endif	 	if(i==count) 	{ 		if(curqty>maxqty) 			maxqty=curqty; 		return; 	} 	if(viable(curqty, curbalance)){ 		back_track(i+1, curqty, curbalance); 		if(curbalance>items[i].price*items[i].qty) 		{ 			picked[i]=true; 			curqty+=items[i].qty; 			curbalance-=items[i].price*items[i].qty; 			back_track(i+1, curqty, curbalance); 			curbalance+=items[i].price*items[i].qty; 			curqty-=items[i].qty; 			picked[i]=false; 		} 	} }  bool greedy_pick() { //	return false; 	int bal, qty; 	bool all_picked=true; 	bal=money; 	qty=0; 	for(int i=0; i<count; ++i) 	{ 		if(bal>items[i].qty*items[i].price) 		{ 			bal-=items[i].qty*items[i].price; 			qty+=items[i].qty; 		}else 			all_picked=false; 	} 	maxqty=qty; 	return all_picked; }    不剪枝版本，供比较理解算法之用  #include <stdio.h> #include <algorithm>  //#define _DEBUG  struct item{ 	int price; 	int qty; 	bool operator <(const item& i){ return price<i.price; } };  item items[16]; int count; int money; int maxqty;  void back_track(int n, int& curqty, int& curbalance); bool greedy_pick();  int main() { 	int curqty, curbalance; 	scanf("%d%d",&money, &count); 	for(int i=0; i<count; ++i) 		scanf("%d%d", &items[i].price, &items[i].qty); 	std::sort(items, items+count); 	maxqty=0; 	curqty=0; 	curbalance=money; 	if(!greedy_pick()) 		back_track(0, curqty, curbalance); 	printf("%d\n", maxqty); 	return 0; }   void back_track(int i, int& curqty, int& curbalance) { 	if(i==count) 	{ 		if(curqty>maxqty) 			maxqty=curqty; 		return; 	} 	// 不选该商品 	back_track(i+1, curqty, curbalance); 	 	// 如果与余额够，选取该商品 	if(curbalance>items[i].price*items[i].qty) 	{ 		curqty+=items[i].qty; 		curbalance-=items[i].price*items[i].qty; 		back_track(i+1, curqty, curbalance); 		curbalance+=items[i].price*items[i].qty; 		curqty-=items[i].qty; 	} }  bool greedy_pick() { 	int bal, qty; 	bool all_picked=true; 	bal=money; 	qty=0; 	for(int i=0; i<count; ++i) 	{ 		if(bal>items[i].qty*items[i].price) 		{ 			bal-=items[i].qty*items[i].price; 			qty+=items[i].qty; 		}else 			all_picked=false; 	} 	maxqty=qty; 	return all_picked; }

user: user-0
content: 我的思路是先排序，再判断   但是我不太清楚该怎么排序，什么优先级不知道  就是该先买什么不知道

user: user-2
content: 感觉是0-1背包的变种，回溯法+贪心算法剪枝，应该不难搞定。