本周学习:加深背包问题,学习二分法
背包问题:
这周主要是在刷题,每天1到2个背包题,背包问题就那些套路,但题千变万化,有的题就是简单的01背包和完全背包,套下来就能对,到了多重背包,就要花些心思了。还有一个很重要,就是我们在看完题后,要辨别出这是个什么背包,他的数量有没有限制,查看限制信息。说实话,这周做的不怎么好,虽然背包问题比之前的dp简单一些,但这个有套路的感觉老是把我引偏,最后还是做不出来,可能是因为自己不够创新,做的题少,一看见题我就开始想着怎么把背包里的那几个套路放进去,可有的题不是那样的,他改变了很多,加了很多限制的条件,就像我们做普通的背包问题简化后用的是维数组,有的题可能需要用到两维,就是感觉做了很多题后,不能只限制于那些基本的套路,还是要具体问题具体分析,一开始做题的时候,基本的那些代码也没搞懂,就开始直接往上写。总之,不是只局限于那些基本的,要懂得创新。有一个题,老师上课讲的,基本的多重背包吧,但这个题的初始化很不一样。作业上的Y题,题目:https://vjudge.net/contest/436354#problem/Y
定义dp[i][j]为买了1~i 组费用为j 是得到的最大价值,这此之中,把dp[0][j]=0,其余定为-1,如果我们一开始把dp初始化为0,则当所有鞋子的价值都是0时,我们就无法区分是买不全那几款鞋子还是能买全但最大价值是0。
状态方程:dp[i][k] = max(dp[i][k], dp[i][k - p[i][j]] + v[i][j]); dp[i][k] = max(dp[i][k], dp[i - 1][k - p[i][j]] + v[i][j]);
代码:
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
using namespace std;
typedef long long ll;
#define INF 0x7fffffff
#define t() cin>>t; while(t--)
#define mem(x) memset(x,INF,sizeof(x))
#define rep(m,n,h) for(int m=n;m<=h;m++)
#define cls(x) memset(x,0,sizeof(x))
const ll maxn=0x7fffffffffffffff;
const int N=20005;
const int mod=1e3+7;
int z,ans,k,n,i,j,dp2[N][N],minn,t,e,f,m;
int temp,sum;
char st[5005];
int a[N],b[N];
map <char,int> sf;
vector<int> p[N], v[N];
int dp[105][N];
int main()
{
// freopen("C:\\Users\\lenovo\\Desktop\\in.txt","r",stdin);
std::ios::sync_with_stdio(false);
while(cin>>n>>m>>t)
{
for(i = 0; i <= t; i++)
{
p[i].clear();
v[i].clear();
}
int a, b, c;
for( i = 0; i < n; ++i)
{
cin>>a>>b>>c;
p[a].push_back(b);
v[a].push_back(c);
}
for(int i = 0; i <= t; ++i)
{
for(int j = 0; j <= m; ++j)
{
if(i == 0)
dp[i][j] = 0;
else dp[i][j] = -1;
}
}
for(int i = 1; i <= t; ++i)
{
for(int j = 0; j < p[i].size(); ++j)
{
for(int k = m; k >= p[i][j]; --k)
{
dp[i][k] = max(dp[i][k], dp[i][k - p[i][j]] + v[i][j]);
dp[i][k] = max(dp[i][k], dp[i - 1][k - p[i][j]] + v[i][j]);
}
}
}
if(dp[t][m] > 0)
cout<<dp[t][m]<<endl;
else cout<<"Impossible"<<endl;
}
return 0;
}
这几天做题,发现了一个问题,就是我习惯用自己写的一个对象,宏定义,头文件留下来,以后好直接用,但有的一些结构体,cmp函数之类的,还有一些对象,在我做题的时候可能会出现一些错误,就像我定义了两个不同类型的a,可能是因为我定义了太多变量了,有的就出错了,所以,之后我就把一些常用的变量对象保存下来,其他的在主函数里定义,这样也提高了容错率。
下面就是二分法了
基本思想就是你想查找一组数据中的一个值,我们就可以分成两块,如果s属于前面那一块,就舍弃后面,如果s属于后面那一块,我们就舍弃前面那一块,循环下来就找到了,但二分法有一个很重要的前提,就是这组数据必须有序,必须有序,必须有序,重要的事情说三遍。
while(high - low > 1.0e-6)
{
mid = (high + low)/2;
if(Caculate(mid)<x)
low=mid;
else
high=mid;
}
基础代码
下面是几个代码技巧
圆周率的表达
#define pi acos(-1)
快速读入
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
inline表示内联函数
普通函数有开栈和清栈,inline函数无开栈和清栈。
当执行开销(代码小)< 调用开销(开栈)时,建议用inline
当执行开销(代码小)> 调用开销(开栈)时,不建议使用inline
总结感想:上面在背包时写了一些,上面提到的就不再说了,这周看ppt的时间少了许多,其他科目的学习也是到了最后了,我不得不抽出时间巩固一下,为了期末的时候好复习。背包问题我感觉我学的不怎么好,可能是花的时间少了些,加上这几天有的题就是想不懂,为什么可以这样写,为什么要这样初始化。背包问题还是要再理解理解才行。二分法嘛,这个倒是挺容易理解的,就是在做题的时候是否能想到用二分法了,毕竟刚学,和自己在做题的时候用还是有区别的。这门课快结束了,更要打起精神来,我想着ACM不仅仅是一门课,课会结束,但我还是想在这方面有所发展。