比赛请见这里。
(虽然是赛时发的,但仍然是赛后题解)
P1216 数字三角形 Number Triangles
这道题是一道最基础不过的迪屁了。如果你使用超级无敌大爆搜的话,那你这道题肯定会 T T T 掉。我们需要把每个数加上它左下角和右下角中最大的数,从倒数第二行开始,一层一层往前递推,答案就出来了。
状态转移方程:
d p [ i ] [ j ] + = m a x { d p [ i + 1 ] [ j ] , d p [ i + 1 ] [ j + 1 ] } dp[i][j]+=max\{dp[i+1][j],dp[i+1][j+1]\} dp[i][j]+=max{dp[i+1][j],dp[i+1][j+1]}
#include<bits/stdc++.h>
using namespace std;
int a[1005][1005];
int main(int argc,char *argv[])
{
int n;
cin >> n;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=i;++j)
{
cin >> a[i][j];
}
}
for(int i=n-1;i>=1;--i)
{
for(int j=1;j<=i;++j)
{
a[i][j] += max(a[i+1][j],a[i+1][j+1]);
}
}
cout << a[1][1];
return 0;
}
(懂的都懂,不懂建议去百度一下)
P1507 NASA的食物计划
这是一道非常经典的01背包型动态规划,就是每个东西只能选一次,求最大值,请点这里。我们首先需要开一个 d p dp dp 二维数组,下标为体积和质量。这时候有些人就要问了:你的博客里面的动态规划那题不是一维数组吗?这里怎么变成二维的了?
这是因为那里只有一个限制条件,但这里有两个限制条件,所以要开二维,当循环完后, d p [ n ] [ m ] dp[n][m] dp[n][m] 就是最优解。
状态转移方程:
d p [ j ] [ k ] = m a x { d p [ j ] [ k ] , d p [ j − a 1 [ i ] ] [ j − a 2 [ i ] ] + v [ i ] } dp[j][k]=max\{dp[j][k],dp[j-a1[i]][j-a2[i]]+v[i]\} dp[j][k]=max{dp[j][k],dp[j−a1[i]][j−a2[i]]+v[i]}
其中第一层循环为 i i i ,第二层循环为 j j j ,第三层循环为 k k k , a 1 a1 a1 为体积, a 2 a2 a2 为质量, v v v 为卡路里。
#include<bits/stdc++.h>
using namespace std;
int dp[405][405],s1,s2,n,a1,a2,v;
int main(int argc,char *argv[])
{
cin >> s1 >> s2 >> n;
for(int i=1;i<=n;++i)
{
cin >> a1 >> a2 >> v;
for(int j=s1;j>=a1;--j)
{
for(int k=s2;k>=a2;--k)
{
dp[j][k] = max(dp[j][k],dp[j-a1][k-a2]+v);
}
}
}
cout << dp[s1][s2];
return 0;
}
P1060 开心的金明
老经典题了,博客里有讲,这里直接贴动态转移方程:
d p [ j ] = m a x { d p [ j ] , d p [ j − v [ i ] ] + x [ i ] dp[j]=max\{dp[j],dp[j-v[i]]+x[i] dp[j]=max{dp[j],dp[j−v[i]]+x[i]
#include<bits/stdc++.h>
using namespace std;
int n,m,w[35],v[35],x[35],f[50000];
int main(int argc,char *argv[])
{
cin >> n >> m;
for(int i=1;i<=m;++i)
{
cin >> v[i] >> w[i];
x[i] = v[i]*w[i];
}
for(int i=1;i<=m;++i)
{
for(int j=n;j>=v[i];--j)
{
f[j]=max(f[j],f[j-v[i]]+x[i]);
}
}
cout << f[n];
return 0;
}
当然你用
d
f
s
dfs
dfs 切掉也可以
P1359 租用游艇
这一题,是求最小值,是一道完全背包,我们在做的时候一定要小心他的顺序,因为他是一个倒三角形。其他的只要想明白,就很容易推出状态转移方程:
d p [ i ] = m i n { d p [ i ] , d p [ j ] + a [ i ] [ j ] } dp[i]=min\{dp[i],dp[j]+a[i][j]\} dp[i]=min{dp[i],dp[j]+a[i][j]}
#include<bits/stdc++.h>
using namespace std;
int n,a[205][205],dp[205];
int main(int argc,char *argv[])
{
cin >> n;
for(int i=1;i<n;++i)
{
for(int j=i+1;j<=n;++j)
{
cin >> a[i][j];
}
dp[i] = 1e9;
}
for(int i=n-1;i>=1;--i)
{
for(int j=i+1;j<=n;++j)
{
dp[i] = min(dp[i],a[i][j]+dp[j]);
}
}
cout << dp[1];
return 0;
}
P1048 采药
(啊这题也太水了吧???)
跟开心的金明一毛一样,只不过限制条件换成了时间。。。
#include<bits/stdc++.h>
using namespace std;
int t,m,a[105],b[105],dp[1005];
int main(int argc,char *argv[])
{
cin >> t >> m;
for(int i=1;i<=m;++i)
{
cin >> a[i] >> b[i];
}
for(int i=1;i<=m;++i)
{
for(int j=t;j>=0;--j)
{
if(j>=a[i])
dp[j] = max(dp[j],dp[j-a[i]]+b[i]);
}
}
cout << dp[t];
return 0;
}
(简单吧?)
好,接下来是唯一不用动态规划的题:
P1095 守望者的逃离
刚看这题你一定特别懵。其实很简单,就是一个人跑步,但他有召唤师技能闪现。他的闪现不用
C
D
CD
CD ,但需要蓝。由于他(它?)回蓝的速度会场快,所以闪现终究是比跑步快的。但比如它离终点只有一米,但他还在等闪现,所以答案就会出错,我们只需要把跑步与闪现分为两个变量,如果闪现比跑步快,那就立马把跑步换成闪现,最后稍加判断即可。。。
#include<bits/stdc++.h>
using namespace std;
int m,s,t;
int main(int argc,char *argv[])
{
cin >> m >> s >> t;
int run=0,shanxian=0;
for(int i=1;i<=t;++i)
{
run += 17;
if(m>=10)
shanxian += 60,m -= 10;
else
m += 4;
if(shanxian>run)
run = shanxian;
if(run>=s)
{
cout << "Yes" << endl << i;
exit(0);
}
}
cout << "No" << endl << run;
return 0;
}
(想用动态规划的小朋朋其实也不是不可以 ……—)
P1077 摆花
这一题比较简单,不讲了。若有人想听,私信发我。
#include<bits/stdc++.h>
using namespace std;
int n,m,a[105],dp[105]={1};
int main(int argc,char *argv[])
{
cin >> n >> m;
for(int i=1;i<=n;++i)
{
cin >> a[i];
}
for(int i=1;i<=n;++i)
{
for(int j=m;j>=0;--j)
{
for(int k=1;k<=min(j,a[i]);++k)
{
dp[j] = (dp[j]+dp[j-k])%1000007;
}
}
}
cout << dp[m];
return 0;
}
P1057 传球游戏
这一题其实想通了就挺简单的。状态转移方程:
d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + d p [ i − 1 ] [ j + 1 ] dp[i][j]=dp[i-1][j-1]+dp[i-1][j+1] dp[i][j]=dp[i−1][j−1]+dp[i−1][j+1]
我们 d p dp dp 数组里存的是每轮中每个人有多少种方案会传到他,而左边的人和右边的人可以一步传到我们这里,所以这个人被传到的方案数就等于左边的人的方案数加上右边的人的方案数,最后判断边界即可。。。
#include<bits/stdc++.h>
using namespace std;
int main(int argc,char *argv[])
{
int n,m,dp[35][35];
memset(dp,0,sizeof(dp));
dp[0][1] = 1;
cin >> n >> m;
for(int i=1;i<=m;++i)
{
for(int j=1;j<=n;++j)
{
if(j==1)
{
dp[i][j] = dp[i-1][n]+dp[i-1][j+1];
}
else if(j==n)
{
dp[i][j] = dp[i-1][j-1]+dp[i-1][1];
}
else
{
dp[i][j] = dp[i-1][j-1]+dp[i-1][j+1];
}
}
}
cout << dp[m][1];
return 0;
}
P2426 删数
数学题不做讲解
#include<bits/stdc++.h>
using namespace std;
int n,a[105],dp[105];
inline int value(int x,int y)
{
return (x==y ? a[x]:abs(a[x]-a[y])*(y+1-x));
}
int main(int argc,char *argv[])
{
cin >> n;
for(int i=1;i<=n;++i)
{
cin >> a[i];
dp[i] = value(1,i);
for(int j=1;j<=i-1;++j)
{
dp[i] = max(dp[j]+value(j+1,i),dp[i]);
}
}
cout << dp[n];
return 0;
}
最后一题,也就是本比赛中最难的一题来了!!!
P1064 金明的预算方案
首先它相较于开心的金明多了一个概念 —— 附件,所以我们要分开存储。由于每个主件最多有 2 2 2 个附件,所以要开二维数组。这道题在写动态转移方程的时候一共要写四次——
1.只买主件
2.只买主件和第一个附件
3.只买主件和第二个附件
4.全买
所以套上 01 模板,开 g i a o giao giao !!!(我把有几个附件存在了 f w [ i ] [ 0 ] fw[i][0] fw[i][0])
#include<bits/stdc++.h> //万能头
using namespace std; //std命名空间
int n,m,dp[35005],w[35005],x[35005],fx[35005][3],fw[35005][3];
//n为钱数,m为要买的东西数量,dp为用来迪屁的数组,w为每个物品的钱数,x为每个物品的乘积,fx为每个附件的乘积,fw为每个附件的钱数
int main(int argc,char *argv[]) //开始了开始了!第一道绿题呢!!!
{
cin >> n >> m; //输入总钱数和东西数量
for(int i=1;i<=m;++i) //循环处理每个东西
{
int v,p,q; //定义变量
cin >> v >> p >> q; //输入变量
if(!q) //如果本身是主件
{
w[i] = v; //赋值
x[i] = v*p; //再赋
}
else //如果是附件
{
fw[q][0] ++; //计数器加一
fw[q][fw[q][0]] = v; //赋值
fx[q][fw[q][0]] = v*p; //赋值
}
}
for(int i=1;i<=m;++i) //迪屁正式开始:枚举每件物品
{
for(int j=n;j>=w[i];--j) //从高到低循环(01背包)
{
dp[j] = max(dp[j],dp[j-w[i]]+x[i]); //只选主件
if(j>=w[i]+fw[i][1])dp[j] = max(dp[j],dp[j-w[i]-fw[i][1]]+x[i]+fx[i][1]); //只选主件和第一个附件
if(j>=w[i]+fw[i][2])dp[j] = max(dp[j],dp[j-w[i]-fw[i][2]]+x[i]+fx[i][2]); //只选主件和第二个附件
if(j>=w[i]+fw[i][1]+fw[i][2])
dp[j] = max(dp[j],dp[j-w[i]-fw[i][1]-fw[i][2]]+x[i]+fx[i][1]+fx[i][2]); //全选
}
}
cout << dp[n]; //此时第n为为最大值
return 0; //程序:哦♂ 我没了
}