一. 实验目的和要求
1.加深对动态规划算法的基本原理的理解,掌握用动态规划方法求解最优化问题的方法步骤及应用;
2.用动态规划设计整数序列的最长递增子序列问题的算法,分析其复杂性,并实现;
3.用动态规划设计求凸多边形的三角剖分问题的算法,分析其复杂性,并实现。
4.选做题:用动态规划设计求解0/1背包问题的算法,分析其复杂性,并实现。
二. 实验步骤
实验一: 最长递增子序列问题:
问题描述:
求一个由n个整数组成的整数序列的最长递增子序列。一个整数序列的递增子序列可以是序列中非连续的数按照原序列顺序排列而成的。 最长递增子序列是其递增子序列中长度最长的。
2.设计与实现
#include
#define mes(a, b) memset(a, b, sizeof a)
#define pb push_back
typedef unsigned long long int ull;
typedef long long int ll;
const int maxn = 1e6 + 20;
const int maxm = 1e5 + 10;
const ll mod = 1e9 + 7;
const ll INF = 1e18 + 100;
const int inf = 0x3f3f3f3f;
const double pi = acos(-1.0);
const double eps = 1e-8;
using namespace std;
int dp[maxn],pre[maxn],a[maxn];
std::vector<int> ans;
int main(){
int _;
for(scanf("%d",&_);_;_--){
ans.clear();
mes(dp,0);mes(pre,-1);
int n;scanf("%d",&n);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]),dp[i] = 1;
for(int i = 1;i <= n;i++){
for(int j = 1;j < i;j++){
if(a[i] > a[j]){
if(dp[i] < dp[j] + 1) dp[i] = dp[j] + 1,pre[i] = j;
}
}
}
int pos,maxx = 0;
for(int i = 1;i <= n;i++){
if(dp[i] > maxx) maxx = dp[i],pos = i;
}
printf("%d\n",maxx);
for(int i = pos;i != -1;i = pre[i]){
ans.pb(a[i]);
}
reverse(ans.begin(), ans.end());
for(auto it : ans) printf("%d ",it);
puts("");puts(" ");
}
}
实验(二) 凸多边形的三角剖分:
问题描述
设P是一个有n个顶点的凸多边形,P中的弦是P中连接两个非相邻顶点的线段。用P中的(n-3)条弦将P剖分成(n-2)个三角形(如下图所示)。使得(n-3)条弦的长度之和最小的三角形剖分称为最优三角剖分。
1.问题分析
要求凸多边形的最优三角剖分,我们先预处理出每个顶点之间的距离,然后考虑区间合并,考虑递归,以i为点的边将凸多边形分为一个三角形和若干个凸多边形,如果k = a + 1,那么剩下的还是一个凸多边形,当k = b – 1时同理,其他情况考虑递归转移式: n u m [ i ] = c a l c ( a , k ) + d i s [ a ] [ k ] + c a l c ( k , b ) + d i s [ k ] [ b ] ; num[i] = calc(a,k) + dis[a][k] + calc(k,b)+dis[k][b]; num[i]=calc(a,k)+dis[a][k]+calc(k,b)+dis[k][b];然后找出最大的num[i]的值即可。复杂度 O ( n 3 ) O(n^3) O(n3)
2.设计与实现
#include
#define mes(a, b) memset(a, b, sizeof a)
#define pb push_back
typedef unsigned long long int ull;
typedef long long int ll;
const int maxn = 550 + 20;
const int maxm = 1e5 + 10;
const ll mod = 1e9 + 7;
const ll INF = 1e18 + 100;
const int inf = 0x3f3f3f3f;
const double pi = acos(-1.0);
const double eps = 1e-8;
using namespace std;
double dis[maxn][maxn];
double x[maxn],y[maxn];
int n;
void init(){
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++){
dis[i][j] = sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
}
}
}
double calc(int b,int c){
double s = INF;std::vector<double> v;
if(c - b == 2) return 0;
for(int i = b + 1;i < c;i++){
if(i == b + 1) v.pb(calc(i,c) + dis[b + 1][c]);
else if(i == c - 1) v.pb(calc(b,i) + dis[b][c - 1]);
else v.pb(calc(i,c) + calc(b,i) + dis[b][i] + dis[i][c]);
}
for(auto it : v) s = min(it,s);
return s;
}
int main(){
int _;
for(scanf("%d",&_);_;_--){
scanf("%d",&n);
for(int i = 1;i <= n;i++) scanf("%lf %lf",&x[i],&y[i]);
init();
printf("%.3f\n",calc(1,n));
}
}
实验(三) 选做题――0/1背包问题:
问题描述
设有一个容量为C的背包,n个物品的集合U={u1, u2, …, un},物品uj的体积和价值分别为sj和vj,C, sj, vj都是正整数。在U中选择物品装入背包,使得装入背包的物品总价值最大。设每种物品或完全装入或完全不装入背包。
1.问题分析
经典的01背包问题,易得转移方程:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i – 1 ] [ j – c [ i ] ] + w [ i ] ) dp[i][j] = max(dp[i- 1][j],dp[i – 1][j – c[i]] + w[i]) dp[i][j]=max(dp[i−1][j],dp[i–1][j–c[i]]+w[i])
记录路径直接用 d p [ ] [ ] dp[][] dp[][]数组求解,看他是从哪个状态转移过来的即可。
当然也可以滚动为一维,也易得
d p [ j ] = m a x ( d p [ j ] , d p [ j – c [ i ] ] + w [ i ] ) ; dp[j] = max(dp[j],dp[j – c[i]] + w[i]); dp[j]=max(dp[j],dp[j–c[i]]+w[i]);
但是滚动成一维就无法倒推了。
2.设计与实现
#include
#define mes(a, b) memset(a, b, sizeof a)
#define pb push_back
typedef unsigned long long int ull;
typedef long long int ll;
const int maxn = 1e6 + 20;
const int maxm = 1e5 + 10;
const ll mod = 1e9 + 7;
const ll INF = 1e18 + 100;
const int inf = 0x3f3f3f3f;
const double pi = acos(-1.0);
const double eps = 1e-8;
using namespace std;
int dp[110][5100],s[maxn],val[maxn];
set<int>ans;
int _;
int main(){
for(scanf("%d",&_);_;_--){
mes(dp,0);ans.clear();
int n,c;scanf("%d %d",&n,&c);
for(int i = 1;i <= n;i++) scanf("%d",&s[i]);
for(int i = 1;i <= n;i++) scanf("%d",&val[i]);
for(int i = 1;i <= n;i++){
for(int j = c;j >= 0;j--){
dp[i][j] = dp[i - 1][j];
if(j < s[i]) continue;
dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - s[i]] + val[i]);
}
}
printf("%d\n",dp[n][c]);
for(int i = n,j = c;i >= 1 && j >= 0;i--){
if(dp[i][j] == dp[i - 1][j - s[i]] + val[i]){
ans.insert(i);j -= s[i];
}
}
for(int i = 1;i <= n;i++) if(ans.count(i)) putchar('1 ');else putchar('0 ');
puts("");
}
}
ps:代码基于c++11,无法运行请打开编译器c++11开关