一 货物搬运
本test唯一贪心的题,剩下四道都是dp——杀了我吧 dp太难了555
俺の代码
#include <iostream>
using namespace std;
int a[100010];
long long ans = 0;
int main()
{
int n;
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 2; i <= n; i++){
if(a[i]>a[i-1])
ans+=a[i]-a[i-1];
}
cout << a[1]+ans;
return 0;
}
二 拉不拉部落
俺の代码
dp[i]表示从1到村庄i所需的最短时间。怎么确定遍历的顺序呢?大概是要确保能遍历到你的终态,以及每次遍历所需的初态都能取到。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
vector<int> dp(1010,1e8);
int n,m;
cin >> n >> m;
dp[1] = 0;
dp[2] = 1;
for(int i = 2; i <= n; i++){
for(int j = 1; j < i; j++){
if(j+m >= i)
dp[i] = min(dp[i],dp[j]+__gcd(j,i));
}
}
cout << dp[n];
return 0;
}
这里的求最大公约数有很多种方法:
1.库函数 比较慢 就跟我上面代码一样写法
2.三目运算符(较快
int gcd(int a,int b)
{
return b>0 ? gcd(b,a%b):a;
}
或者
inline int gcd(int a,int b)
{
return b==0 ? a:gcd(b,a%b);
}
3.位运算(更快
int gcd(int a,int b)
{
while(b^=a^=b^=a%=b);
return a;
}
这题用floyd或者dijsktra做也行。
三 荣荣的背包
就是01背包基础上要装满而已。【但是我还是卡了很久……orz 最后发现实现起来其实很简单,只是在初始dp数组时赋值一个绝对值足够大的负数即可【但是dp[0]要额外赋值为0】。
俺的代码
#include <iostream>
#include <algorithm>
#include <memory.h>
#include <string.h>
#include <vector>
using namespace std;
int vi[1010],wi[1010];
int main()
{
int n,v;
cin >> n >> v;
for(int i = 1; i <= n; i++) cin >> vi[i] >> wi[i];
vector<int> dp(1010,-4000);
dp[0] = 0;
for(int i = 1; i <= n; i++){
for(int j = v; j >= vi[i]; j--){
dp[j] = max(dp[j],dp[j-vi[i]]+wi[i]);
}
}
if(dp[v] < 0)
cout << "0";
else
cout << dp[v];
return 0;
}
memset()
如果用memset(a,1,20),就是对a指向的内存的20个字节进行赋值,每个都用数1去填充,转为二进制后,1就是00000001,占一个字节。一个int元素是4字节,合一起是0000 0001,0000 0001,0000 0001,0000 0001,转化成十六进制就是0x01010101,就等于16843009,就完成了对一个int元素的赋值了。
四 时北北的上升子序列
os.没时间做了就没做…… :(
放个佬の的代码
#include <iostream>
#include <cstring>
using namespace std;
const int N=1010;
int f[N];//长度为i的最长不上升(下降)子序列最后一位
int a[N];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
f[0]=2e9;
int ans=0;
for(int i=0;i<n;i++)
{
int l=0,r=ans;
//找到第一个比他大的
while(l<r)
{
int m=l+r+1>>1;
if(f[m]<a[i])
{
r=m-1;
}
else
{
l=m;
}
}
f[l+1]=a[i];
ans=max(l+1,ans);
}
cout<<ans<<endl;
return 0;
}
五 大恺的购物之旅
俺の代码:
.其实也是01背包,在基础上加几个条件判断。选主件/附件二选一/附件全买
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int v[51],p[51],q[51];//价钱、满意度、附属商品
int a[51][2],b[51][2];//附属商品价钱和满意度
int n,m;
vector<int> dp(50010,0);
int main()
{
cin >> n >> m;
// 用户输入
for(int i = 1; i <= m; i++){
cin >> v[i] >> p[i] >> q[i];
if(q[i] != 0)
for(int j = 1; j <= q[i]; j++)
cin >> a[i][j] >> b[i][j];
}
for(int i = 1; i <= m; i++){
for(int j = n; j >= v[i]; j--){
// 如果有附属品
if(q[i]){
int m = j-v[i];
if(q[i] == 1){
// 只有一个附属品,买附件1
if(m >= a[i][1])
dp[j] = max(dp[j],dp[m-a[i][1]]+p[i]+b[i][1]);
}
if(q[i]==2){//两个附属品
int sum = a[i][1]+a[i][2];
// 买其中一个
if(m >= a[i][1])
dp[j] = max(dp[j],dp[m-a[i][1]]+p[i]+b[i][1]);
if(m >= a[i][2])
dp[j] = max(dp[j],dp[m-a[i][2]]+p[i]+b[i][2]);
if(m >= sum)
// 买俩
dp[j] = max(dp[j],dp[m-sum]+p[i]+b[i][1]+b[i][2]);
}
}
dp[j] = max(dp[j],dp[j-v[i]]+p[i]);
}
}
cout << dp[n];
return 0;
}
小结
对于动态规划的掌握还是很烂,经典问题的算法实现也没有很理解or熟悉,对于怎么写出遍历的结构及先后顺序不明确,状态转移方程有时难以明确,还是要多看视频多练题。