磁帶最大利用率問題——動態規劃

O_o勳 發表於 2020-11-21

題目描述

設有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]