T1.P1216 [USACO1.5][IOI1994]数字三角形 Number Triangles
需要思考的地方:
1.这题显然可以用dp解,只需要找到两个状态的递推关系
2.设a[i][j]是从末尾到第i行第j列的最大和,只需要求出a[0][0]
3.由于到了一个点只有加上这个点左下角和加上这个点右下角两种状态(很显然因为要求最大值所以不可能选择不加),故有a[i][j]+=max(a[i+1][j],a[i+1][j+1])
思路:
1.先初始化这个三角形
2.从倒数第二排向上搜,取左下或右下较大者加到对应元素上
3.最后输出a[0][0]即可
代码如下:
#include<bits/stdc++.h>
using namespace std;
int r,a[1005][1005];
int main()//从后往前搜
{
cin>>r;
for(int i=0;i<r;i++)
{
for(int j=0;j<=i;j++)
cin>>a[i][j];
}
for(int i=r-2;i>=0;i--)//下标志从n-2开始,因为最后一排不用去推
{
for(int j=0;j<=i;j++)
a[i][j]+=max(a[i+1][j],a[i+1][j+1]);
}
cout<<a[0][0];
}
T2.P1048 [NOIP2005 普及组] 采药
需要思考的地方:
1.可以用动态规划来做,接下来要找递推关系
2.碰到一种药材,只能选择是采或者不采
3.如果拿,那么总价值加上对应价值,总空间减去对应空间。如果不拿,那这个状态跟上个状态一样。所以可以得出递推公式dp[j]=max(dp[j],dp[j-cost]+value),其中dp[i]表示在i时间里采集的最大价值(ps.这题一开始用dp[i][j] = max(dp[i-1][j], dp[i-1][j-time[i]] + worth[i])也可以)
思路:
一个一个输入草药价值和占用空间,每次输一个就进行一次判断,选择摘或不摘其中的最大值
另外一种二维数组和下面“疯狂的采药”做法一样就不在这里写了
代码如下(有空间优化和无空间优化两类):
#include<bits/stdc++.h>//这好像直接空间优化了
using namespace std;
int dp[1005];//dp[t]:在t时间内采的最大价值
int t,m,cost,value;
int main()
{
cin>>t>>m;
for(int i=0;i<m;i++)//一个一个判断
{
cin>>cost>>value;
for(int j=t;j>=0;j--)
{
if(j>=cost)
dp[j]=max(dp[j],dp[j-cost]+value);
}
}
cout<<dp[t];
}
//anthoer solution
/*#include<iostream>
#include<algorithm>
using namespace std;
int dp[1009][1009];
int main(){
int t, m;
int time[1009], worth[1009];
cin >> t >> m;
for( int i=0; i<m; i++ ){
cin >> time[i] >> worth[i];
}
for( int i=0; i<m; i++ ){
for( int j=0; j<t; j++ ){
if(j < time[i]){ // 必须考虑这种情况, 否则wa; 但空间优化可完美解决这个问题
dp[i][j] = dp[i-1][j];
}else{
dp[i][j] = max(dp[i-1][j], dp[i-1][j-time[i]] + worth[i]);
}
}
}
cout << dp[m][t];
return 0;
}
T3.p1616疯狂的采药![](https://i-blog.csdnimg.cn/blog_migrate/85621f6a969969ef98ef5a88f268eb31.png)
思路
和上面那题差不多,但是这题用二维数组来解(因为比较好想)
递推分两类来考虑:
dp[i][j]表示在前i个药材里,空间为j时能取得的最大价值
1.遇到这个药材的时候背包空间不够了,这时候只能不拿,dp[i][j]=dp[i-1][j]
2.背包空间还够装,这时候取max(拿之后的状态,不拿之后的状态)即可。也就是说递推为:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-time[i]] + worth[i]
代码如下:
#include<bits/stdc++.h>
using namespace std;
const long long N=1e7;
long long m;
long long t;
struct pick
{
long long cost;
long long value;
}arr[10005];
long long ans[N];
int main()
{
cin>>t>>m;
for(long long i=0;i<m;i++)
cin>>arr[i].cost>>arr[i].value;
for(long long i=0;i<m;i++)
{
for(long long j=arr[i].cost;j<=t;j++)
ans[j]=max(ans[j],ans[j-arr[i].cost]+arr[i].value);
}
cout<<ans[t];
}
T4.LCS
需要思考的问题:
1.肯定dp能做(因为在就学dp,还能用啥)
2.遇到一个字符只有一样or不一样两种可能,一样的话这个状态的lcs就等于上个状态的lcs加上1
不一样的话就取上个状态其中一个字符串往下找一位的最大字符列数的值(因为每次进行一步都要至少把一个字符串向后搜一位,不然进行这步就没意义了)
3.所以递推公式就是
if(s1[i-1]==s2[j-1]) dp[i][j]=dp[i-1][j-1]+1
else dp[i][j]=max(dp[i-1][j],dp[i][j-1])
4.但是这题还要还原出路径,就得搜完后在往前找有几个一样的并且打印
思路:
1.先初始化俩字符串
2.遍历,找出lcs大小
3.用双指针再遍历回去,碰到一样的就打印
代码:
int dp[1005][1005];
int len1,len2;
string s1,s2;
char ans[1005];
void lcs()//先求lcs的长度
{
for(int i=1;i<=len1;i++)//要从1开始遍历,不然会出现下标为负数的情况
{
for(int j=1;j<=len2;j++)
{
if(s1[i-1]==s2[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[len1][len2];
}
void find()
{
int i=len1,j=len2,k=0;//k用来记录答案个数
while(dp[i][j]>0)
{
if(s1[i-1]==s2[j-1])//因为字符串第一个下表是0所以要-1
{
ans[k++]=s1[--i];//这样的k是从后往前找的,所以等下要反向输出
j--;
}
else
{
if(dp[i-1][j]<dp[i][j-1]) j--;
else i--;
}
}
for(int i=k-1;i>=0;i--)//总共k个,所以从k-1开始
cout<<ans[i];
}
int main()
{
cin>>s1>>s2;
len1=s1.size();
len2=s2.size();
lcs();
find();
}
T5.P1115 最大子段和![](https://i-blog.csdnimg.cn/blog_migrate/da30ceade43dec2711ce11168e4b0497.png)
需要思考的地方:
1.对于给的一个数,只有两种状态,加上或者不加(怎么感觉一直在说这句话)
2.怎么记录最大值-一边输入数据一边记录
3.如果输入的数大于零,那肯定得加上,但是如果小于零的话:
1.加上这个数以后整体还是大于零--可以加(因为我们是一边记录一边往后加的,max的值 每次都会更新,这就是说当前的max值其实是加上这个负数之前的值)
2.加上这个数之后整体小于零--全丢,在从零开始计算(因为往后不管是正数负数这个状态都 会把它(后面的状态)变小一点,所以不可能包含进最大值)
思路:
一边输入一边计算,方法就跟上面说的一样
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2*1e5;
int n,a[N],sum=0;
int maxn=-INT_MAX;
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
if(sum<0) sum=0;//表明从此时开始算子序列,sum的值为当前的数
sum+=a[i];
if(sum>maxn) maxn=sum;
}
cout<<maxn;
}