您的位置:首页 > 其它

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

2015-12-19 17:23 302 查看
参考文章:http://blog.csdn.net/u013480600/article/details/40376143

自己写的AC代码如下:

#include<bits/stdc++.h>
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<bits/stdc++.h>
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
[t0-1].n+1 << " " << F
[t0-1].len +678 << endl;
}
return 0;
}


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

#include<bits/stdc++.h>
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
[t0-1].n+1 << " " << F
[t0-1].len +678 << endl;
}
return 0;
}


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

#include<bits/stdc++.h>
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
[t0-1].n+1 << " " << F
[t0-1].len +678 << endl;
}
return 0;
}


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

#include<bits/stdc++.h>
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<bits/stdc++.h>
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: