UVa12563_劲歌金曲(动态规划_01背包变形)

参考文章:http://blog.csdn.net/u013480600/article/details/40376143

自己写的AC代码如下:

#include
using namespace std;
const int maxt = 50 * 3 * 60 + 5;
typedef struct { 
	int n;
	int len;
}Node;
Node F[maxt];
Node best(Node& x, Node& y){
	if(x.n < y.n) return y;
	else if(x.n == y.n && x.len<=y.len) return y;
	else return x;
}
int main() {
	int T;
	scanf("%d", &T);
	int count = 0;
	while(T--) {
		int n, t0, t;
		scanf("%d%d", &n, &t0);
		memset(F, 0, sizeof(F)); 
		int max_t;
		max_t = min(maxt, t0);
		for(int i = 1; i <= n; i++) {
			scanf("%d", &t);
			for(int j = max_t-1; j>= 0; j--) {
				if(i==1){
					F[j].n = 0; 
					F[j].len = 0;
				}else {
					F[j] = F[j];
				}
				if(j >= t) {
					Node tmp;
					tmp.n = F[j-t].n + 1;
					tmp.len = F[j-t].len + t;
					F[j] = best(F[j], tmp);
				} 
			}
		}
		printf("Case %d: %d %d\n", ++count, F[t0-1].n+1, F[t0-1].len+678);
	} 
	return 0;
}
 


初始版本:

#include
using namespace std;
//状态F[i][j]:前i首歌曲所达到的最优状态
//这个最优状态包括两个小的最优状态
//1.最大歌曲数 、 2.最长歌曲时间 
//状态转移方程:F[i][j] = 最优(F[i-1][j], F[i-1][j-t[i]] + t[i]); 
const int maxn = 50 + 5;
const int maxt = 50 * 3 * 60;
int t[maxn];//每首歌曲时间
typedef struct {//状态节点定义 
	int n;//1.最大歌曲数 
	int len;//2.最长歌曲时间 
}Node;
Node F[maxn][maxt]; //状态节点 
Node best(Node& x, Node& y){
	if(x.n < y.n) return y;
	else if(x.n == y.n && x.len<=y.len) return y;
	else return x;
}
int main() {
	int T;
	cin >> T;
	int count = 0;
	while(T--) {
		int n, t0;
		cin >> n >> t0;
		//F数组的初始化;
		memset(F, 0, sizeof(F)); 
		for(int i = 0; i <= n; i++) {
			cin >> t[i];
		}
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= t0-1; j++) {
				if(i==1){
					F[i][j].n = 0;
					F[i][j].len = 0;
				}else {
					F[i][j] = F[i-1][j];
				}
				Node tmp;
				if(j >= t[i]) {
					tmp.n = F[i-1][j-t[i]].n + 1;
					tmp.len = F[i-1][j-t[i]].len + t[i];
				}
				if(j >= t[i] && tmp.len <= (t0-1)) F[i][j] = best(F[i][j], tmp);//两个子状态的比较 
				//cout << "F["<< i <<"]["<< j <<"] = " << F[i][j].len << endl; //打印状态 
			}
		}
		cout << "Case " << ++count << ": " << F[n][t0-1].n+1 << " " << F[n][t0-1].len +678 << endl;
	} 
	return 0;
}
 

优化版本1:整理了判断条件

#include
using namespace std;
//状态F[i][j]:前i首歌曲所达到的最优状态
//这个最优状态包括两个小的最优状态
//1.最大歌曲数 、 2.最长歌曲时间 
//状态转移方程:F[i][j] = 最优(F[i-1][j], F[i-1][j-t[i]] + t[i]); 
const int maxn = 50 + 5;
const int maxt = 50 * 3 * 60;
int t[maxn];//每首歌曲时间
typedef struct {//状态节点定义 
	int n;//1.最大歌曲数 
	int len;//2.最长歌曲时间 
}Node;
Node F[maxn][maxt]; //状态节点 
Node best(Node& x, Node& y){
	if(x.n < y.n) return y;  //先要保证歌曲数最大的情况下 ,再保证总时间最长 
	else if(x.n == y.n && x.len<=y.len) return y;
	else return x;
}
int main() {
	int T;
	cin >> T;
	int count = 0;
	while(T--) {
		int n, t0;
		cin >> n >> t0;
		memset(F, 0, sizeof(F));
		for(int i = 1; i <= n; i++) {
			cin >> t[i];//输入可以放在这里   能够这样写取决于状态的设定(状态要求i是顺序枚举) 
			for(int j = 0; j <= t0-1; j++) {
				if(i==1){   //边界一定要记得处理 
					F[i][j].n = 0;
					F[i][j].len = 0;
				}else {
					F[i][j] = F[i-1][j];
				}
				if(j >= t[i]) {
					Node tmp;
					tmp.n = F[i-1][j-t[i]].n + 1;
					tmp.len = F[i-1][j-t[i]].len + t[i];
					F[i][j] = best(F[i][j], tmp);//两个子状态的比较
				} 
//				tmp.len <= (t0-1)这个条件的判断实际上是不需要的,之所以前面一个代码会加上,
//				就是因为对自己定义的这个状态还不是很清楚
//				临时状态tmp 是在之前.前i-1首歌曲在j-t[i]的时间内达到的最优状态F[i-1][j-t[i]]的基础上 
//				再加上i这首歌曲的时间t[i]所达到的新状态,也就是说tmp状态就是F[i][j]的一种决策(增加第i首歌曲),而
//				F[i][j]的另一种决策自然就是F[i-1][j](即不增加第i首歌曲),所以无论是哪一个tmp,他的时间即
//				tmp.len一直都是j, j的变化范围是1~(t0-1), 自然就不会超过t0-1这个值,所以 
//				tmp.len <= (t0-1)这个判断条件是多余的。 
//				cout << "F["<< i <<"]["<< j <<"] = " << F[i][j].len << endl; //打印状态 
			}
		}
		cout << "Case " << ++count << ": " << F[n][t0-1].n+1 << " " << F[n][t0-1].len +678 << endl;
	} 
	return 0;
}
 

优化版本2:边读入边计算

#include
using namespace std;
//状态F[i][j]:前i首歌曲所达到的最优状态
//这个最优状态包括两个小的最优状态
//1.最大歌曲数 、 2.最长歌曲时间 
//状态转移方程:F[i][j] = 最优(F[i-1][j], F[i-1][j-t] + t[i]); 
const int maxn = 50 + 5;
const int maxt = 50 * 3 * 60 + 5;
//顺序读入  边读边计算使得可以不要t[maxn]这个数组,所以可以直接用t代替当前第i首歌曲的时间 
typedef struct {//状态节点定义 
	int n;//1.最大歌曲数 
	int len;//2.最长歌曲时间 
}Node;
Node F[maxn][maxt]; //状态节点 
Node best(Node& x, Node& y){
	if(x.n < y.n) return y;  //先要保证歌曲数最大的情况下 ,再保证总时间最长 
	else if(x.n == y.n && x.len<=y.len) return y;
	else return x;
}
int main() {
	int T;
	cin >> T;
	int count = 0;
	while(T--) {
		int n, t0, t;
		cin >> n >> t0;
		memset(F, 0, sizeof(F)); 
		for(int i = 1; i <= n; i++) {
			cin >> t;//输入可以放在这里   能够这样写取决于状态的设定(状态要求使得i是顺序枚举) 
			for(int j = 0; j <= t0-1; j++) {
				if(i==1){   //边界一定要记得处理 
					F[i][j].n = 0; 
					F[i][j].len = 0;
				}else {
					F[i][j] = F[i-1][j];
					//无论如何,先使当前状态(F[i][j])等于相同时间j下不加上当前这首歌(i)的最优状态F[i-1][j] 
				}
				if(j >= t) {
					Node tmp;
					tmp.n = F[i-1][j-t].n + 1;
					tmp.len = F[i-1][j-t].len + t;
					F[i][j] = best(F[i][j], tmp);//两个子状态的比较
				}  
			}
		}
		cout << "Case " << ++count << ": " << F[n][t0-1].n+1 << " " << F[n][t0-1].len +678 << endl;
	} 
	return 0;
}
 

优化版本3:使用滚动数组优化空间

#include
using namespace std;
//状态F[j]:前i首歌曲所达到的最优状态
//这个最优状态包括两个小的最优状态
//1.最大歌曲数 、 2.最长歌曲时间 
//状态转移方程:F[j] = 最优(F[j], F[i-1][j-t] + t); 
//F数组是从上到下、从右往左计算的。在计算F[i][j]之前,F[j]中保存的就是F[i-1][j]的值,所以可以把它写成一维
//参考紫书p273图(9-6) 这个就是滚动数组,优化了空间   注意使用滚动数组时  j一定要逆序枚举   不然后面的F元素
//会因为前面已经更新的F元素而再次更新 (倒序就不会发生这样的情况),从而发生错误。!!! 
//const int maxn = 50 + 5;F变成一维数组  ,这个也就省略了 
const int maxt = 50 * 3 * 60 + 5;
//顺序读入  边读边计算使得可以不要t[maxn]这个数组,所以可以直接用t代替当前第i首歌曲的时间 
typedef struct {//状态节点定义 
	int n;//1.最大歌曲数 
	int len;//2.最长歌曲时间 
}Node;
Node F[maxt]; //状态节点 
Node best(Node& x, Node& y){
	if(x.n < y.n) return y;  //先要保证歌曲数最大的情况下 ,再保证总时间最长 
	else if(x.n == y.n && x.len<=y.len) return y;
	else return x;
}
int main() {
	int T;
	cin >> T;
	int count = 0;
	while(T--) {
		int n, t0, t;
		cin >> n >> t0;
		//F数组的初始化;
		memset(F, 0, sizeof(F));   //这一句其实也可以不要  想一想这个数组的更新过程就知道了
		//不过最好还是写上,以区别每一组测试数组就清空一下F状态数组 
		for(int i = 1; i <= n; i++) {
			cin >> t;//输入可以放在这里   能够这样写取决于状态的设定(状态要求使得i是顺序枚举) 
			for(int j = t0-1; j>= 0; j--) {
				if(i==1){   //边界一定要记得处理 
					F[j].n = 0; 
					F[j].len = 0;
				}else {
					F[j] = F[j];
					//无论如何,先使当前状态(F[i][j])等于相同时间j下不加上当前这首歌(i)的最优状态F[i-1][j] 
				}
				if(j >= t) {
					Node tmp;
					tmp.n = F[j-t].n + 1;
					tmp.len = F[j-t].len + t;
					F[j] = best(F[j], tmp);//两个子状态的比较
				} 
			}
		}
		cout << "Case " << ++count << ": " << F[t0-1].n+1 << " " << F[t0-1].len +678 << endl;
	} 
	return 0;
}
 

优化版本4:边界时间

//涉及结构体整体赋值,用C++写更方便 
#include
using namespace std;
const int maxt = 50 * 3 * 60 + 5;
typedef struct { 
	int n;
	int len;
}Node;
Node F[maxt];
Node best(Node& x, Node& y){
	if(x.n < y.n) return y;
	else if(x.n == y.n && x.len<=y.len) return y;
	else return x;
}
int main() {
	int T;
	scanf("%d", &T);
	int count = 0;
	while(T--) {
		int n, t0, t;
		scanf("%d%d", &n, &t0);
		memset(F, 0, sizeof(F)); 
		int max_t;
		max_t = min(maxt, t0); //两个时间   一个  是怎么都不会超过的maxt   一个是给出的时间t0 
		//说了不超过50首歌   且  每首不超过3分钟 而且每首最多只唱一遍  只要判maxt-1和t0-1中最小的那个作为j最值就行。 
		for(int i = 1; i <= n; i++) {
			scanf("%d", &t);
			for(int j = max_t-1; j>= 0; j--) {
				if(i==1){
					F[j].n = 0; 
					F[j].len = 0;
				}else {
					F[j] = F[j];
				}
				if(j >= t) {
					Node tmp;
					tmp.n = F[j-t].n + 1;
					tmp.len = F[j-t].len + t;
					F[j] = best(F[j], tmp);
				} 
			}
		}
		printf("Case %d: %d %d\n", ++count, F[t0-1].n+1, F[t0-1].len+678);
	} 
	return 0;
}
 


你可能感兴趣的:(UVa12563_劲歌金曲(动态规划_01背包变形))