题目
网格图上的DP
POJ - 1163 The Triangle
链接:http://poj.org/problem?id=1163
题意:给定一个数字三角形,求从顶点到达低端,能够获得的最大权值。
思路:设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示在位置 ( i , j ) (i,j) (i,j) 到达最低端能够获得的最大权值。
d p [ i ] [ j ] = m a x ( d p [ i + 1 ] [ j ] , d p [ i + 1 ] [ j + 1 ] ) + a [ i ] [ j ] dp[i] [ j ] =max(dp[ i+1 ][ j ],dp [ i+1][j+1])+a[i][j] dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+a[i][j]
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int n;
int dp[105][105],a[105][105];
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; ++i)
for(int j=1; j<=i; ++j)
scanf("%d",&a[i][j]);
for(int i=n; i>=1; --i)
for(int j=1; j<=i; ++j)
dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+a[i][j];
printf("%d\n",dp[1][1]);
return 0;
}
POJ - 滑雪
链接:http://poj.org/problem?id=1088
题意:给定一个图,图中数字代表每点的高度,求最长的可滑行的长度。
思路:dp[i][j]表示从(i,j)这点出发的最长滑行长度。从低到高,遍历每一个点,往四周更新(我为人人递推型),或者四周向我更新(人人为我递推型)。
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
]
+
1
,
d
p
[
i
]
[
j
]
)
dp[i][j]=max(dp[i-1][j]+1,dp[i][j])
dp[i][j]=max(dp[i−1][j]+1,dp[i][j])
#include <algorithm>
#include <cstdio>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int n,m;
int a[105][105],dp[105][105];
int dx[]= {1,-1,0,0};
int dy[]= {0,0,1,-1};
struct Node
{
int v,x,y;
bool operator<(const Node &b) const
{
return v<b.v;
}
} p[105*105];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j)
{
scanf("%d",&a[i][j]);
p[(i-1)*m+j].v=a[i][j];
p[(i-1)*m+j].x=i;
p[(i-1)*m+j].y=j;
dp[i][j]=1;
}
sort(p+1,p+1+n*m);
for(int i=1; i<=n*m; ++i)
{
int v=p[i].v,x=p[i].x,y=p[i].y;
for(int j=0; j<4; ++j)
{
int nx=x+dx[j],ny=y+dy[j];
if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&a[nx][ny]<v)
dp[x][y]=max(dp[x][y],dp[nx][ny]+1);
}
}
int ans=0;
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j)
ans=max(ans,dp[i][j]);
printf("%d\n",ans);
return 0;
}
线性DP
POJ - 2533 Longest Ordered Subsequence
链接:http://poj.org/problem?id=2533
题意:给定一个序列,求最长上升子序列的长度。
思路:设
d
p
[
i
]
dp[i]
dp[i] 表示以
a
i
a_i
ai 终点的最长上升子序列的长度。
d
p
[
i
]
=
m
a
x
(
d
p
[
j
]
,
1
≤
j
<
i
且
a
j
<
a
i
)
dp[i]=max(dp[j],1\leq j<i 且 a_j<a_i)
dp[i]=max(dp[j],1≤j<i且aj<ai)
POJ - 1458 Common Subsequence
链接:http://poj.org/problem?id=1458
题意:给定两个字符串 a、b,求出两个字符串最长公共子序列的长度。
思路:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示 a 中前
i
i
i 个字符和 b 中前
j
j
j 个字符形成的最长公共子序列的长度。
d
p
[
i
]
[
j
]
=
{
d
p
[
i
−
1
]
[
j
−
1
]
+
1
(
a
[
i
]
=
=
b
[
j
]
)
m
a
x
(
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
]
[
j
−
1
]
)
(
a
[
i
]
≠
b
[
j
]
)
dp[i][j]=\begin{cases} dp[i-1][j-1]+1 &(a[i]==b[j]) \\ max(dp[i-1][j],dp[i][j-1]) &(a[i] \neq b[j]) \end{cases}
dp[i][j]={dp[i−1][j−1]+1max(dp[i−1][j],dp[i][j−1])(a[i]==b[j])(a[i]=b[j])
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string>
#define ll long long
using namespace std;
const int maxn=1010;
string a,b;
int dp[maxn][maxn];
int main()
{
while(cin>>a>>b)
{
int n=a.length();
int m=b.length();
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j)
if(a[i-1]==b[j-1]) dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
cout<<dp[n][m]<<endl;
}
return 0;
}
背包DP
2755:神奇的口袋
链接:http://bailian.openjudge.cn/practice/2755
题意:给定 n 个物品中,求取得体积为 40 的方案数。
思路:dp[i][j]表示前 i 个物品,体积为 j 的方案数。
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
]
+
d
p
[
i
−
1
]
[
j
−
v
[
i
]
]
(
j
≥
v
[
i
]
)
dp[i][j]=dp[i-1][j]+dp[i-1][j-v[i]] (j\ge v[i] )
dp[i][j]=dp[i−1][j]+dp[i−1][j−v[i]](j≥v[i])
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int n;
int a[30],dp[30][50];
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; ++i) scanf("%d",&a[i]);
dp[0][0]=1;
for(int i=1; i<=n; ++i)
for(int j=0; j<=40; ++j)
{
dp[i][j]=dp[i-1][j];
if(j>=a[i]) dp[i][j]+=dp[i-1][j-a[i]];
}
printf("%d\n",dp[n][40]);
return 0;
}