动态规划
A - 上台阶2
小瓜想走上一个一共有n级的台阶,由于小瓜的腿长比较特殊,他一次只能向上走1级或者3级或者5级台阶。小瓜想知道他有多少种方法走上这n级台阶,你能帮帮他吗?
Input
一行一个整数n(n<=100000),表示一共有n级台阶。
Output
一行一个整数,表示小瓜上台阶的方案数对100003取余的结果。
Sample
Inputcopy Outputcopy
3 2
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int n;
cin>>n;
vector<long long>dp(n+1);
dp[0]=1;
dp[1]=1;
dp[3]=1;
dp[5]=1;
for(int i=2;i<=n;i++)
{
if(i>=3&&i>=5)
{
dp[i]=dp[i-1]+dp[i-3]+dp[i-5];
}
else
{
if(i>=3)
{
dp[i]=dp[i-1]+dp[i-3];
}
else
dp[i]=dp[i-1];
}
dp[i]%=100003;
}
cout<<dp[n];
return 0;
}
B - 数字三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
(图1)
图1给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。
注意:路径上的每一步只能从一个数走到下一层上和它最近的左边的那个数或者右边的那个数。
Input
输入的是一行是一个整数N (1 < N <= 100),给出三角形的行数。下面的N行给出数字三角形。数字三角形上的数的范围都在0和100之间。
Output
输出最大的和。
Sample
Inputcopy
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Outputcopy
30
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int n;
cin>>n;
vector<vector<int> >dp(n+1,vector<int>(n+1));
vector<vector<int> >a(n+1,vector<int>(n+1));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
cin>>a[i][j];
}
}
for(int i=0;i<=n;i++)
{
dp[i][0]=0;
dp[0][i]=0;
}
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+a[i][j];
ans=max(ans,dp[i][j]);
}
}
cout<<ans;
return 0;
}
C - 矩阵取数问题
一个N*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,从左上走到右下,只能向下向右走,求能够获得的最大价值。
例如:3 * 3的方格。
1 3 3
2 1 3
2 2 1
能够获得的最大价值为:11。
Input
第1行:N,N为矩阵的大小。(2 <= N <= 500) 第2 - N + 1行:每行N个数,中间用空格隔开,对应格子中奖励的价值。(1 <= N[i] <= 10000)
Output
输出能够获得的最大价值。
Sample
Inputcopy
3
1 3 3
2 1 3
2 2 1
Outputcopy
11
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int n;
cin>>n;
vector<vector<int> >dp(n+1,vector<int>(n+1));
vector<vector<int> >a(n+1,vector<int>(n+1));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cin>>a[i][j];
}
}
for(int i=0;i<=n;i++)
{
dp[i][0]=0;
dp[0][i]=0;
}
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1])+a[i][j];
ans=max(ans,dp[i][j]);
}
}
cout<<ans;
return 0;
}
D - 背包问题
在N件物品取出若干件放在容量为W的背包里,每件物品的体积为W1,W2……Wn(Wi为整数),与之相对应的价值为P1,P2……Pn(Pi为整数)。求背包能够容纳的最大价值。
其中1 <= N <= 100,1 <= W <= 10000,每个物品1 <= Wi, Pi <= 10000。
Input
第1行输入两个整数N和W; 第2 ~ N+1行,每行两个整数Wi和Pi,分别表示每个物品的体积和价值。
Output
输出可以容纳的最大价值。
Sample
Inputcopy
3 6
2 5
3 8
4 9
Outputcopy
14
#include<iostream>
#include<vector>
using namespace std;
int weight[110];
int value[110];
int dp[110][10010];
int main()
{
int n,W;
cin>>n>>W;
for(int i=1;i<=n;i++)
{
cin>>weight[i]>>value[i];
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=W;j++)
{
dp[i][j]=dp[i-1][j];
if(j>=weight[i])
{
dp[i][j]=max(dp[i-1][j-weight[i]]+value[i],dp[i-1][j]);
}
}
}
cout<<dp[n][W];
return 0;
}
E - 完全背包
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是v[i],价值是c[i]。
现在请你选取一些物品装入背包,使这些物品的体积总和不超过背包容量,且价值总和最大。
其中1<=N<=100,1<=V<=50000,1<=v[i],c[i]<=10000。
Input
第一行输入两个数N,V,分别表示物品种类数和背包容积; 之后N行,每行两个数v[i],c[i],分别表示第i种物品的体积和价值;
Output
输出一个数,表示最大的价值
Sample
Inputcopy
2 11
2 3
6 14
Outputcopy
20
#include<iostream>
#include<vector>
using namespace std;
int weight[110];
int value[110];
int dp[50050];
int main()
{
int n,W;
cin>>n>>W;
for(int i=1;i<=n;i++)
{
cin>>weight[i]>>value[i];
}
for(int i=1;i<=n;i++)
{
for(int j=weight[i];j<=W;j++)
{
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
}
}
cout<<dp[W];
return 0;
}
F - 背包问题 V2
有N种物品,每种物品的数量为C1,C2…Cn。从中任选若干件放在容量为W的背包里,每种物品的体积为W1,W2…Wn(Wi为整数),与之相对应的价值为P1,P2…Pn(Pi为整数)。求背包能够容纳的最大价值。
其中1 <= N <= 100,1 <= W <= 50000,1 <= Wi, Pi <= 10000, 1 <= Ci <= 200。
Input
第1行,2个整数,N和W中间用空格隔开。N为物品的种类,W为背包的容量。 第2 ~ N+1行,每行3个整数,Wi,Pi和Ci分别是物品体积、价值和数量。
Output
输出可以容纳的最大价值。
Sample
Inputcopy
3 6
2 2 5
3 3 8
1 4 1
Outputcopy
9
#include<iostream>
#include<vector>
using namespace std;
vector<int>w;
vector<int>v;
vector<int>c;
int dp[50050];
int main()
{
int n,W;
cin>>n>>W;
w.push_back(0);
v.push_back(0);
c.push_back(0);
for(int i=1;i<=n;i++)
{
int wi,vi,ci;
cin>>wi>>vi>>ci;
w.push_back(wi);
v.push_back(vi);
c.push_back(ci);
}
for(int i=1;i<=n;i++)
{
if(c[i]>1)
{
for(int j=1;j<=c[i]-1;j++)
{
w.push_back(w[i]);
v.push_back(v[i]);
}
}
}
for(int i=1;i<w.size();i++)
{
for(int j=W;j>=w[i];j--)
{
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
cout<<dp[W];
return 0;
}
G - 最长上升子序列
一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8).
你的任务,就是对于给定的序列,求出最长上升子序列的长度。
Input
输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。
Output
最长上升子序列的长度。
Sample
Inputcopy
7
1 7 3 5 9 4 8
Outputcopy
4
#include<iostream>
#include<vector>
using namespace std;
vector<int>w;
vector<int>v;
vector<int>c;
int dp[50050];
int main()
{
int n,W;
cin>>n>>W;
w.push_back(0);
v.push_back(0);
c.push_back(0);
for(int i=1;i<=n;i++)
{
int wi,vi,ci;
cin>>wi>>vi>>ci;
w.push_back(wi);
v.push_back(vi);
c.push_back(ci);
}
for(int i=1;i<=n;i++)
{
if(c[i]>1)
{
for(int j=1;j<=c[i]-1;j++)
{
w.push_back(w[i]);
v.push_back(v[i]);
}
}
}
for(int i=1;i<w.size();i++)
{
for(int j=W;j>=w[i];j--)
{
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
cout<<dp[W];
return 0;
}
H - 最长公共子序列
对于两个给定的序列,请求出它们的最长公共子序列长度。
一个序列的子序列定义为能通过删除一部分元素,保留剩下的元素相对顺序不变而得到的序列。
输入格式
第一行两个整数 n,mn,m,表示两个序列的长度。
第二行 nn 个整数 a_1,a_2, \ldots ,a_na
1
,a
2
,…,a
n
,表示第一个序列。
第二行 mm 个整数 b_1,b_2, \ldots ,b_mb
1
,b
2
,…,b
m
,表示第二个序列。
输出格式
输出两个序列的最长公共子序列长度。
样例
Inputcopy Outputcopy
4 5
1 2 4 5
4 1 3 3 2
2
数据范围与提示
本题共有两个子任务。
对于所有数据,1 \leq n,m,a_i,b_i \leq 700001≤n,m,a
i
,b
i
≤70000。
Subtask 1(50分):n,m \leq 5000n,m≤5000。
Subtask 2(50分):无特殊限制。
数据是瞎随机的,可能挺弱的,欢迎hack。
#include<iostream>
#include<vector>
using namespace std;
vector<int>w;
vector<int>v;
int main()
{
int n,m;
cin>>n>>m;
w.push_back(0);
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
w.push_back(x);
}
v.push_back(0);
for(int i=1;i<=m;i++)
{
int x;
cin>>x;
v.push_back(x);
}
vector<vector<int> >dp(n+1,vector<int>(m+1));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(w[i]==v[j])
{
dp[i][j]=dp[i-1][j-1]+1;
}
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
//cout<<dp[i]<<" ";
}
cout<<dp[n][m];
return 0;
}
J - 循环数组最大子段和
N个整数组成的循环序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续的子段和的最大值(循环序列是指n个数围成一个圈,因此需要考虑a[n-1],a[n],a[1],a[2]这样的序列)。当所给的整数均为负数时和为0。
例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。
Input
第1行:整数序列的长度N(2 <= N <= 50000) 第2 - N+1行:N个整数 (-10^9 <= S[i] <= 10^9)
Output
输出循环数组的最大子段和。
Sample
Inputcopy
6
-2
11
-4
13
-5
-2
Outputcopy
20
#include<iostream>
#include<vector>
#include<limits.h>
using namespace std;
int n;
typedef long long ll;
ll fun(vector<int>&nums,vector<ll>&dp)
{
ll ans=0;
for(int i=1;i<=n;i++)
{
if(dp[i-1]>=0)
dp[i]=dp[i-1]+nums[i];
else
dp[i]=nums[i];
ans=max(ans,dp[i]);
//cout<<dp[i]<<" ";
}
return ans;
}
int main()
{
cin>>n;
vector<int>nums(n+1);
vector<ll>dp(n+1);
for(int i=1;i<=n;i++)
{
cin>>nums[i];
}
ll sum=0;//记录总和
ll ans=0;
ll p=0,q=0;
ll maxa=0;//记录最大子段和
ll maxb=0;//记录最小子段和
for(int i=1;i<=n;i++)
{
sum+=nums[i];
p+=nums[i];
q+=nums[i];
if(p<0)
p=0;
maxa=max(maxa,p);
if(q>0)
q=0;
maxb=min(maxb,q);
}
// cout<<sum<<endl;
// cout<<maxa<<" "<<maxb<<endl;
ans=max(maxa,sum-maxb);
cout<<ans;
return 0;
}
I - 石子合并
二.有N堆石子,每堆的重量是a[i],排成一条直线,每次只能合并相邻的两堆,直到合成一堆为止,问最后的最小花费是多少。
#include<iostream>
#include<vector>
#include<limits.h>
using namespace std;
typedef long long ll;
int INF=99999999;
int main()
{
int n;
cin>>n;
vector<int>w(n+1);//石子
vector<int>sum(n+1);
for(int i=1;i<=n;i++)
{
cin>>w[i];
sum[i]=sum[i-1]+w[i];
}
vector<vector<int> >dp(n+1,vector<int>(n+1));
for(int i=1;i<=n;i++)//初始化
{
for(int j=1;j<=n;j++)
{
dp[i][j]=INF;
}
}
for(int i=1;i<=n;i++)
dp[i][i]=0;
/*dp[i][j]表示石子i到石子j合并花费的最小代价*/
for(int len=2;len<=n;len++)//长度
{
for(int i=1;i<=n-len+1;i++)
{
int j=i+len-1;
for(int k=i;k<j;k++)
{
//划分成i到k,k+1到j的最小花费之和加上i到j本身的代价
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
}
}
}
cout<<dp[1][n];
return 0;
}