一.区间dp
感想:最近做的区间dp的题实在太少啦,因为对这一方面的dp题我只是对最基础的题型有了解,但对于比较复杂的区间dp首先我的思路比较受限,看到一个题后无法将它由一个大区间划分为小区间,并找出状态转移方程。对于老师给出的csdn的链接我也看了一部分,但有些题看了代码对于思路的想法也还是一知半解,没有理解到区间dp的实质,导致换一道题即使本质相似,也还是想不出思路和代码,所以还是要多见题,多了解别人做题的思路,并归纳总结出一类题的相同之处,再自己做题打代码。
1.
题意:在x轴上有n个客人,每个客人每秒增加的愤怒值不同。给出客人和餐厅的位置,以及客人每分钟增加的愤怒值,和送餐行走一公里需要的时间,问送完n个客人的外卖最小愤怒值
思路:
把餐厅所在的点加进去,然后按照在x轴上的位置排序。从餐厅所在位置向左右开始DP
dp[i][j][0]表示从i到j用户送到最小不开心值,此时送货员停留在左边即i位置
dp[i][j][1]表示从i到j用户送到最小不开心值,此时送货员停留在右边即j位置
状态有四种,
dp[i][j][0] = min(dp[i][j][0],dp[i+1][j][0] + (a[i+1].x-a[i].x)*(sum[n]-sum[j]+sum[i]));
dp[i][j][0] = min(dp[i][j][0],dp[i+1][j][1] + (a[j].x-a[i].x)*(sum[n]-sum[j]+sum[i]));
dp[i][j][1] = min(dp[i][j][1],dp[i][j-1][0] + (a[j].x-a[i].x)*(sum[n]-sum[j-1]+sum[i-1]));
dp[i][j][1] = min(dp[i][j][1],dp[i][j-1][1] + (a[j].x-a[j-1].x)*(sum[n]-sum[j-1]+sum[i-1]));
2.
题意:有一群屌丝,每个屌丝有个屌丝值,如果他第K个上场,屌丝值就为Diaosi[i]*(k-1),通过一个小黑屋来调整,使最后总屌丝值最小。
思路:设dp[i][j]表示从第i个人到第j个人这段区间的最小花费(是只考虑这j-i+1个人,不需要考虑前面有多少人)
那么对于dp[i][j]的第i个人,就有可能第1个上场,也可以第j-i+1个上场。考虑第K个上场
即在i+1之后的K-1个人是率先上场的,那么就出现了一个子问题 dp[i+1][i+k-1]表示在第i个人之前上场的
对于第i个人,由于是第k个上场的,那么屌丝值便是a[i]*(k-1)
其余的人是排在第k+1个之后出场的,也就是一个子问题dp[i+k][j],对于这个区间的人,由于排在第k+1个之后,所以整体愤怒值要加上k*(sum[j]-sum[i+k-1])
二.闲碎知识点总结
1.构造函数重载
构造函数是可以重载的,即可以写多个构造函数,它们的参数表不同。当编译到能生成对象的语句时,编译器会根据这条语句所提供的参数信息决定该调用哪个构造函数。如果没有提供参数信息,编译器就认为应该调用无参构造函数。
class Complex{
private:
double real, imag;
public:
Complex(double r);
Complex(double r, double i);
Complex(Complex cl, Complex c2);
};
Complex::Complex(double r) //构造函数 1
{
real = r;
imag = 0;
}
Complex :: Complex(double r, double i) //构造数 2
{
real = r;
imag = i;
}
Complex :: Complex(Complex cl, Complex c2) //构造函数 3
{
real = cl.real + c2.real;
imag = cl.imag + c2.imag;
}
int main(){
Complex cl(3), c2(1,2), c3(cl,c2), c4 = 7;
return 0;
}
根据参数个数和类型要匹配的原则,c1、c2、c3、c4 分别用构造函数 1、构造函数 2、构造函数 3 和构造函数 4 进行初始化。初始化的结果是:c1.real = 3,c1.imag = 0 (不妨表示为 c1 = {3, 0}),c2 = {1, 2},c3 = {4, 2}, c4 = {7, 0}。
2.构造函数初始化列表
初始化列表位于构造函数的参数表之后,函数体之前:
构造函数(参数表):初始化列表{函数体}
class Student
{
private:
int m_num,m_maths ;
public:
Student(){}//默认构造函数
Student(int num,int maths ):m_num(num),m_maths(maths)
{
cout<<m_num<<endl;
cout<<m_maths<<endl;
}
};
3.const用法
(1)定义常量 : 可以为所有的内置类型对象使用const限定符,可以用const代替#define来定义常量
const int buf=100;
(2)const限定指针的两种用法:(1)限定指针指向的对象 (2)限定指针中存放的内容
const修饰指针变量时:
(1)只有一个const,如果const位于*左侧,表示指针所指数据是常量,不能通过解引用修改该数据;指针本身是变量,可以指向其他的内存单元。
(2)只有一个const,如果const位于*右侧,表示指针本身是常量,不能指向其他内存地址;指针所指的数据可以通过解引用修改。
(3)两个const,*左右各一个,表示指针和指针所指数据都不能修改。
const int*pl; //P1是一个指向const int的指针,指向常量对象
int const*pl; //效果同上
int d=l;
int* const p2=&d; //P2是一个指向int的指针常量,一直指向d
const int* const p3=&d; //指针与其指向的对象都是常量
3. 限定函数参数(const修饰符也可以修饰函数的传递参数)
void Fun(const int Var); //告诉编译器Var在函数体中的无法改变.
4.限定函数返回值(const修饰符也可以修饰函数的返回值,是返回值不可被改变)
const int Fun1(); const MyClass Fun2();
5.限定对象
限定的对象不能被修改,不能调用非成员函数。
6. 限定数据成员
限定的数据成员在创建对象时初始化,之后值不能修改。
7.限定成员函数
限定的成员函数不会修改数据成员的值,可以被const对象调用,也可以被非const对象调用。
定义const成员函数的语法形式为:
返回类型 成员函数名(参数表) const {函数体}
class X{
int m;
public:
X(int v=0):m(v){}
void set(int v){m=v;}
int get() const {return m;}
};
...
const X b(5); //const 对象
b.get(); //正确
b.srt(10); //错误
const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数。