题目描述
设有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;
}
有点类似背包问题,不过有三种状态转移的情况:
- 如果个数更多则直接装入,[i][j]=[i-1][j-pro_len[i]]
- 如果个数相同则取两个状态的max,[i][j]=max([i-1][j],[i-1][j-pro_len[i]])
- 直接继承状态[i][j]=[i-1][j]