---
tags: APCS Preparation
---
# Dynamic Programming
## Maximum Subarray
### Problem Statement
給定一個陣列有10個元素,請找出最大的連續子陣列和,結果會在$-10^8$到$10^8$之間,陣列元素不會都是小於0的數字
### Sample I/O
Input
```
1 2 3 4 5 -10 20 30 -40 10
```
Output
```
55
```
### 解題想法
加總到sum < 0 時,該結果沒有幫助
所以歸零後再繼續加,如果新的加總結果比原本的max還要大 -> 更新max值
```cpp
#include <iostream>
using namespace std;
int main()
{
int arr[10];
while(1)
{
for(int i=0;i<10;i++)
{
cin>>arr[i];
}
int max = -200000000;
int sum = 0;
for(int i=0;i<10;i++)
{
sum+=arr[i];
if(sum>max)
{
max = sum;
}
if(sum<0)
{
sum = 0;
}
}
cout<<max<<endl;
}
}
```
## 01 Knapsack Problem
### Problem Statement
假設有n個物品跟一個背包,已知背包的負重能力與每個物品的價值與重量,物品只能取或不取,不能只取物品的一部分,且每樣物品只有一個,求在書包的負重能力範圍內放入背包所有物品的最大價值
###
```cpp
#include<iostream>
#include<cstdlib>
using namespace std;
int v[101],w[101],k[1001];
int ks,n;
int main()
{
while(cin>>n)
{
for(int i=0;i<n;i++)
{
cin>>w[i]>>v[i];
}
cin>>ks;
memset(k, 0, sizeof(k));
for(int i=0;i<n;i++)
{
for(int j=ks;j>=w[i];j--) //必須要大於等於w[i]的才能討論
{
if(k[j-w[i]]+v[i]>k[j])
{
k[j] = k[j-w[i]]+v[i];
}
}
}
cout<<"max_val:"<<k[ks]<<endl;
}
}
```
## 01 Knapsack Problem 考慮最佳解路徑
```cpp
#include<iostream>
using namespace std;
int v[101],w[101],k[1001],p[101][1001];
int ks,n; //maximum_weight
int main()
{
while(cin>>n)
{
for(int i=0;i<n;i++)
{
cin>>w[i]>>v[i];
}
cin>>ks;
memset(k,0,sizeof(k));
memset(p,-1,sizeof(p));
for(int i=0;i<n;i++)
{
for(int j=ks;j>=w[i];j--)
{
if(k[j-w[i]]+v[i]>k[j])
{
k[j] = k[j-w[i]] + v[i];
p[i][j] = i;
}
}
}
cout<<"書包的最大價值為"<<k[ks]<<endl;
//取得放書包路徑
int j = ks;
for(int i=n-1;i>=0;i--)
{
if(p[i][j]>=0)
{
cout<<"put "<<i+1<<" th item into the knapsack"<<endl;
j = j - w[i];
}
}
}
}
```
## Money Changing Problem
### Problem Statement
某國家有n個硬幣面額,請你計算達成目標金額x的最少硬幣個數,所輸入的n種硬幣面額一定可以達成目標金額x
### Input
每次輸入數字n,n表示硬幣面額個數,n小於10,之後n後每一行分別有一個整數v,表示硬幣的面額。最後輸入一個整數x,表示目標金額,且x小於50000
### Output
輸出一個整數,表示達成目標金額x的最少硬幣個數
### Sample Input
4
4
1
9
12
20
### Sample Output
4
```cpp
#include<iostream>
#include<cstdlib>
using namespace std;
int main()
{
int n;
while(cin>>n)
{
int v[n],x;
for(int i=0;i<n;i++)
{
cin>>v[i];
}
cin>>x;
int m[50001];
memset(m,0x6f,sizeof(m));
m[0] = 0;//初始化~重要!
for(int j=0;j<n;j++)
{
for(int r=v[j];r<=x;r++)
{
m[r] = min(m[r],m[r-v[j]]+1); //加入此貨幣跟不加入方法數的min
}
}
cout<<m[x]<<endl;
}
}
```
```cpp
//考慮路徑時的解法
#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;
int m[50001],p[50001];
int v[1001],x;
int main()
{
int n;
while(cin>>n)
{
for(int i=0;i<n;i++)
{
cin>>v[i];
}
cin>>x;
memset(m,0x6f,sizeof(m));
memset(p,0,sizeof(p));
m[0] = 0;
for(int j=0;j<n;j++)
{
for(int r=v[j];r<=x;r++)
{
if(m[r] > m[r-v[j]]+1)
{
//cout<<"ready!"<<endl;
p[r] = v[j];
}
m[r] = min(m[r],m[r-v[j]]+1);
}
}
cout<<m[x]<<endl;
int tmp = x;
while(tmp>0)
{
cout<<p[tmp]<<" ";
tmp-=p[tmp];
}
cout<<endl;
}
}
```
## Longest Common Subsequence
### Input
每次輸入兩個英文字串,求這兩個字串的最長共同子序列,兩字串長度都不會超過 100 chars
### Output
輸出一個整數,表示lcs長度
### Sample Input
```
comp
zope
```
### Sample Output
```
2
```
### 解題思路
> 參考來源:http://web.ntnu.edu.tw/~algo/LongestCommonSubsequence.html
有兩字串s1, s2 要找出兩字串的LCS,記為LCS(s1,s2)
取出兩字串最後一個元素
> $i.e.$
> $s_{1} = sub_{1} + e_1$
> $s_2 = sub_2 + e_2$
分情形討論
| 情形 | $lcs(s1,s2)$ |
| ------------------- |:--------------------:|
| LCS包含e1,不包含e2 | $lcs(s1,sub_2)$ |
| LCS包含e2,不包含e1 | $lcs(sub_1,s2)$ |
| LCS不包含e1和e2 | $lcs(sub_1,sub_2)$ |
| LCS包含e1且e2 | $lcs(sub_1,sub_2)+1$ |
統整
```cpp
LCS(s1, s2) =
{ max( LCS(sub1, s2), LCS(s1, sub2) ) , when e1 != e2
{ LCS(sub1, sub2) + e1 , when e1 == e2
```
```cpp
#include<iostream>
#include<cstdlib>
#include<string>
using namespace std;
int main ()
{
string a,b;
while(1)
{
getline(cin,a);
getline(cin,b);
long int s1 = a.length();
long int s2 = b.length();
int lcs[s1+1][s2+1];
memset(lcs, 0, sizeof(lcs));
lcs[0][0] = 0;
for(int i=0;i<s1;i++)
{
for(int j=0;j<s2;j++)
{
if(a[i] == b[j])
{
lcs[i+1][j+1] = lcs[i][j] + 1;
}
else
{
lcs[i+1][j+1] = max(lcs[i+1][j],lcs[i][j+1]);
}
}
}
cout<<lcs[s1][s2]<<endl;
}
}
```
```cpp
//包含輸出該lcs
#include<iostream>
#include<cstdlib>
#include<string>
using namespace std;
int main()
{
string s1,s2;
while(1)
{
getline(cin,s1);
getline(cin,s2);
long long int len1 = s1.length();
long long int len2 = s2.length();
int lcs[len1+1][len2+1];
memset(lcs,0,sizeof(lcs));
lcs[0][0] = 0;
string r = "";
for(int i=0;i<len1;i++)
{
for(int j=0;j<len2;j++)
{
if(s1[i] == s2[j])
{
lcs[i+1][j+1] = lcs[i][j]+1;
r+=s1[i];
}
else
{
lcs[i+1][j+1] = max(lcs[i+1][j],lcs[i][j+1]);
}
}
}
cout<<lcs[len1][len2]<<endl;
cout<<r<<endl;
}
}
```