思路:使用動態規劃尋找到考慮所有問題後,機器A執行\(i\)分鐘後,機器B執行的時間的最小值。之後再在所有的這\(i\)種情況中找到機器A和機器B共同執行的最小值。
子問題:
\(dp[i][j]\)表示在做前i個任務中,機器A執行\(j\)分鐘的情況下B機器執行的最短時間
子問題之間的轉移方程:
\[dp[i][j] = min(dp[i-1][j]+b[i],dp[i-1][j-a[i]])
\]
解釋:
在考慮第\(i\)個任務時,機器B在機器A執行\(j\)分鐘的情況下的最小值應該是
前\(i-1\)個任務機器B在A執行\(j\)分鐘的情況下的最小值加上B機器執行第\(i\)個任務的時間
以及將這個任務交給A機器執行前\(i-1\)個任務中A機器執行\(j-a[i]\)分鐘時,B機器執行時間的最小值。
最優子結構的證明:
當\(dp[i][j]\)取到最優時,如果前\(i-1\)個任務機器B在A執行\(j\)分鐘的最短時間\(dp[i-1][j]\)還可以更短,或者如果前\(i-1\)個任務機器B在A執行\(j-a[i]\)分鐘的最短時間還可以更短,那麼\(dp[i][j]\)的值就可以更小,與假設矛盾。
為何本問題的解一定對應著機器B的時間要取最小值?
反證法:\(dp[n][j]\)是問題的解,對應著機器B執行時間的最小值。假設此時機器B的執行時間可以恰當增大,以此來使得總時間最小。那麼,有下列三種情況:
- 如果此時機器B執行的時間本來就比機器A執行的時間更長,如果機器B的執行時間變長,那麼顯而易見,這會使得結果變差,因此不成立。
- 如果此時機器B執行的時間比機器A更短,機器B執行時間變長而機器A執行時間也變長或不變,這不會使結果變得更好,因此不成立。
- 如果此時機器B執行的時間比機器A更短且機器B執行時間變長而機器A執行時間變短,那麼一定可以在\(dp[n][j]\)這個序列中找到對應的\(j'<j\)使得\(dp[n][j']<dp[n][j]\),那麼\(dp[n][j]\)不會是該問題的解,因此矛盾。
演算法複雜度分析:
一共有\(n\)種物品,所有物品的和為\(s\),因此子問題的規模大概是\(n*s\)個,解決一個子問題需要的複雜度為\(O(1)\),因此演算法複雜度為\(O(sn)\),由於\(s\)可以特別大,所以這個演算法不適用於\(s\)特別大的情況。
程式碼如下:
#include <iostream>
#include <algorithm>
using namespace std;
int dp[105][10024];
int a[105];
int b[105];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
cin >> b[i];
int sum = 0;
for (int i = 1; i <= n; i++)
{
sum += a[i]; //只考慮前i個,因此花時間最多不會超過前i個之和
for (int j = 0; j <= sum; j++)
{
int cur_val = 0x3f3f3f3f;
if (j - a[i] >= 0)
cur_val = dp[i - 1][j - a[i]];
dp[i][j] = dp[i - 1][j] + b[i];
if (dp[i][j] > cur_val)
dp[i][j] = cur_val;
}
}
int ans = 0x3f3f3f3f;
for (int i = 0; i <= sum; i++)
if (ans > max(dp[n][i], i))
ans = max(dp[n][i], i);
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= sum; j++)
cout<<dp[i][j]<<" ";
cout<<endl;
}
cout << ans << endl;
return 0;
}