Dynamic Programming[0]
建中資訊讀書會
- 225班
- 電子計算機研習社_學術長
- 綽號807
- 資讀講師(第0堂)
這我
這是甚麼?
- 動態規劃
- DP
- 從小問題的答案推得大答案
Dynamic Programming
某貓要走n階樓梯,一次能向上跨一階或二階,有幾種走法?
1
0
5
2
3
4
1
1
2
3
5
8
走法數
階梯
dp[x]=dp[x-1]+dp[x-2]
轉移式
定狀態
定狀態
- 初始值
- 小問題的解
定狀態
- 推得當前答案
- 可利用已知
轉移式
#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
signed main(){
int n;
cin>>n;
vector<int> dp(100,0);
dp[0]=dp[1]=1;
for(int s=2;s<100;s++) dp[s]=dp[s-1]+dp[s-2];
for(int s,k=0;k<n;k++){
cin>>s;
cout<<dp[s]<<"\n";
}
}
# PRESENTING CODE
你已經會了
通靈轉移式
寫出使狀態
講完了下課
常見dp問題
網格路徑問題
有n條縱向及m條橫向的長方形路網中,求捷徑走法數。
網格路徑問題
dp[n][m]紀錄到i、j格上的走法數
初始 dp[0][0]=1;
轉移式 dp[i][j]=dp[i-1][j]+dp[i][j-1];
事實上可以C n+m取n
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int pow(int x,int y){
int ans=1;
for(int base=x,s=1;s<=y;s<<=1,base=base*base%mod) if(s&y) ans=ans*base%mod;
return ans;
}
int divide(int x,int y){
return x*pow(y,mod-2)%mod;
}
signed main(){
int n,m;
cin>>n>>m;
if(n==1||m==1){
cout<<1;
return 0;
}
int d[n+m];
d[0]=1;
for(int s=1;s<n+m;s++) d[s]=d[s-1]*(s+1)%mod;
cout<<divide(divide(d[n+m-3],d[n-2]),d[m-2]);
}
# PRESENTING CODE
網格路徑問題
網格路徑問題
背包問題
有一個背包,承重上限為x,現在有n個東西可以選擇是否放進背包,給你每個東西的重量w[i]和價值c[i],輸出你能裝下的最大價值。
背包問題
0/1背包問題
dp[i]紀錄花費時間為i時拯救的最大價值
初始 dp[m+1]皆設為0
每次考慮第s個包包
轉移式 dp[i]=max(dp[i],dp[i-t[s]]+v[s])
i由大到小才不會重複計算
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
int n,m,sum;
cin>>n>>m;
vector<int> v(n),t(n);
vector<int> dp(m+1,0);
for(int &i:v) cin>>i,sum+=i;
for(int &i:t) cin>>i;
for(int s=0;s<n;s++){
for(int d=m;d>=t[s];d--){
dp[d]=max(dp[d],dp[d-t[s]]+v[s]);
}
}
cout<<sum-dp[m];
return 0;
}
# PRESENTING CODE
每種東西都有無限個
無限背包問題
要改哪裡?
無限背包問題
i由小到大才會重複計算
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
vector<int> copied;
void ms(int l,int r,vector<int> &data){
if(r-l==1) return;
int mid=l+r>>1,e=mid-l;
ms(l,mid,data),ms(mid,r,data);
if(copied.size()<e) copied.resize(e);
for(int s=0;s<e;s++) copied[s]=data[s+l];
for(int s=l,sl=0,sr=mid;sl<e;s++){
if(sr==r||copied[sl]<=data[sr]) data[s]=copied[sl++];
else data[s]=data[sr++];
}
return;
}
int main(){
ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n,x;
cin>>n>>x;
vector<int> ways(x+1);
vector<int> coins(n);
for(int s=0;s<n;s++) cin>>coins[s];
ms(0,n,coins);
ways[0]=1;
for(int s=1;s<=x;s++){
for(int d=0;d<n&&s-coins[d]>=0;d++){
ways[s]+=ways[s-coins[d]];
ways[s]%=mod;
}
}
cout<<ways[x]<<endl;
}
# PRESENTING CODE
不要問我為甚麼要手刻合併排序
每種東西有c[i]個
有限背包問題
一個一個算 這好像有點蠢
怎麼拆?
1、2、4、8、16、32、...&剩下的
有限背包問題
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,t;
cin>>n;
vector<int> w(n),c(n),m(n);
for(int i=0;i<n;i++) cin>>w[i]>>m[i]>>c[i];
cin>>t;
vector<int> dp(t+1,0);
for(int i=0,mm,mw;i<n;i++){
for(int a=1;a<c[i];c[i]-=a,a<<=1){
mm=m[i]*a,mw=w[i]*a;
for(int j=t;j>=mw;j--) dp[j]=max(dp[j],dp[j-mw]+mm);
}
mm=m[i]*c[i],mw=w[i]*c[i];
for(int j=t;j>=mw;j--) dp[j]=max(dp[j],dp[j-mw]+mm);
}
cout<<dp[t]<<"\n";
}
# PRESENTING CODE
LIS
Longest Increasing Subsequence
給你n個數,求最長遞增子序列
最常遞增子序列
lis[]就是LIS
每次比較x[i]
比所有LIS中的元素小: lis[0]=x[i]
比所有LIS中的元素大: lis[lislen++]=x[i]
否則: 將LIS中最小大於x[i]者=x[i]
#include<bits/stdc++.h>
using namespace std;
int main(){
ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n;
cin>>n;
vector<int> x(n,0);
vector<int> lis(n,1e9+7);
int Max=1;
for(int &i:x) cin>>i;
lis[0]=x[0];
for(int &i:x){
if(i==lis[Max-1]||i==lis[0]) continue;
else if(i>lis[Max-1]) lis[Max++]=i;
else if(i<=lis[0]) lis[0]=i;
else for(int l=0,r=Max,mid=l+r>>1;r;mid=l+r>>1){
if(r-l==1) lis[r]=i,r=0;
else if(lis[mid]<i) l=mid;
else if(lis[mid]>i) r=mid;
else break;
}
}
cout<<Max<<endl;
}
# PRESENTING CODE
Edit Distance
給你字串A、B,有三種操作
- 增加一個字元
- 刪除一個字元
- 更改一個字元
求最低操作次數使兩字串相同
Edit Distance
dp[i][j]代表到A[i]、B[j]兩字串更改至完全相同所需的最少操作次數
對於每個i、j dp[i][0]=i; dp[0][j]=j;
轉移式
A[i]和B[j]相同:
dp[i][j]=min(dp[i-1][j]+1, dp[i][j-1]+1,dp[i-1][j-1])
不同:
dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+1)
#include<bits/stdc++.h>
using namespace std;
int main(){
string a,b;
cin>>a>>b;
int la=a.size(),lb=b.size();
//cout<<la<<" "<<lb<<endl;
a=" "+a;
b=" "+b;
vector<vector<int>> dp(la+1,vector<int> (lb+1,1e9+7));
dp[0][0]=0;
for (int i = 0; i <= la; ++i) dp[i][0] = i; // 加上這裡
for (int i = 0; i <= lb; ++i) dp[0][i] = i; // 加上這裡
for(int x=1;x<=la;x++) for(int y=1;y<=lb;y++){
dp[x][y]=min(dp[x][y],dp[x-1][y]+1);
dp[x][y]=min(dp[x][y],dp[x][y-1]+1);
if(a[x]==b[y]) dp[x][y]=min(dp[x][y],dp[x-1][y-1]);
else dp[x][y]=min(dp[x][y],dp[x-1][y-1]+1);
}
cout<<dp[la][lb];
}
# PRESENTING CODE
Dynamic Programming0
By 建中店自計算機研習社學術長807⁸⁰⁷
Dynamic Programming0
- 253