磁带最大利用率问题——动态规划

题目描述

设有n个程序{1,2,…,n}要存放在长度为L的磁带上。程序i存放在磁带上的长度是li,1<=i<=n.

程序存储问题要求确定这n个程序在磁带上的一个存储方案,使得能够在磁带上存储尽可能多的程序。在保证存储最多程序的前提下,要求磁带的利用率最大。

编程任务:对于给定的n个程序存放在磁带上的长度,编程计算磁带上最多可以存储的程序数和占用磁带的长度。

输入

第一行是2个正整数,分别表示文件个数n和磁带长度L。第二行中,有n个正整数,表示程序存放在磁带上的长度。

输出

第一行输出最多可以存储的程序数和占用磁带的长度;第二行输出存放在磁带上的每个程序的长度,(输出程序次序应与输入数据次序保持一致)

本来不想写的博客,无奈网上大部分代码都是贪心,就只将最后一个换成最大能容纳的程序就是了。
给出

4 50
22 23 24 25

这个样例很明显就错了。
这题应该是动态规划:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include<algorithm>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define G 6.67430*1e-11
#define  rd read()
#define pi 3.1415926535
using namespace std;
const ll mod = 998244353;
inline ll read() {
	ll x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch>'9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 1) + (x << 3) + (ch ^ 48);
		ch = getchar();
	}
	return x * f;
}
ll gcd(ll a, ll b) { return !b ? a : gcd(b, a % b); }
int dp[605][6005][2];
//dp[i][j][0]表示在程序数n、磁带长度为j的情况下 ,最多可以存储的程序数
// dp[i][j][1]表示在程序数n、磁带长度为j的情况下 ,程序最多情况时的磁带长度 
int Count[605];
int pro_len[605];
int n, len;
int main() {
	FILE* fp;
	fp = fopen("input.txt", "r");
	fscanf(fp, "%d %d", &n, &len);
	for (int i = 1; i <= n; i++)
	{
		fscanf(fp, "%d", &pro_len[i]);
	}
	memset(dp, 0, sizeof(dp));
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j <= len; j++)
		{
			if (pro_len[i] <= j && dp[i - 1][j][0] < dp[i - 1][j - pro_len[i]][0] + 1)//第一个状态转移,如果个数更多则直接装入,[i][j]=[i-1][j-pro_len[i]]
			{
				dp[i][j][0] = dp[i - 1][j - pro_len[i]][0] + 1;
				dp[i][j][1] = dp[i - 1][j - pro_len[i]][1] + pro_len[i];
			}
			else if (pro_len[i] <= j && dp[i - 1][j][0] == dp[i - 1][j - pro_len[i]][0] + 1)//第二个状态转移,如果个数相同则取两个状态的max,[i][j]=max([i-1][j],[i-1][j-pro_len[i]])
			{
				dp[i][j][0] = dp[i - 1][j][0];
				dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - pro_len[i]][1] + pro_len[i]);
			}
			else//否则直接继承状态
			{
				dp[i][j][0] = dp[i - 1][j][0];
				dp[i][j][1] = dp[i - 1][j][1];
			}
			cout << dp[i][j][0] <<' ';
		}
		cout << endl;
	}
	cout << endl;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j <= len; j++)
		{
			cout << dp[i][j][1] << ' ';
		}

		cout << endl;
	}
	fp = fopen("output.txt", "w");
	fprintf(fp, "%d %d\n", dp[n][len][0], dp[n][len][1]);
	int j = dp[n][len][1], k = 1, i = n;
	while (i)
	{
		if (j>=pro_len[i]&&i&&dp[i][j][0] == dp[i - 1][j - pro_len[i]][0] + 1 && dp[i][j][1]== dp[i - 1][j - pro_len[i]][1] + pro_len[i])
		{
			j -= pro_len[i];
			Count[k++] = pro_len[i];
		}
		i--;
	}
	for (k = 1; k <= dp[n][len][0]; k++)
	{
		if (k != 1)fprintf(fp, " ");
		fprintf(fp, "%d", Count[k]);
	}
	fprintf(fp, "\n");
	return 0;
}

有点类似背包问题,不过有三种状态转移的情况:

  1. 如果个数更多则直接装入,[i][j]=[i-1][j-pro_len[i]]
  2. 如果个数相同则取两个状态的max,[i][j]=max([i-1][j],[i-1][j-pro_len[i]])
  3. 直接继承状态[i][j]=[i-1][j]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值