本人是个oi蒟蒻,如果有什么不对的地方望指正
这次我做了几道动态规划题,留个记录
1.筷子(传送门:http://www.caioj.cn/problem.php?id=1077)
首先这一题我们先定义一个数组dp[i][j],表示前i只筷子里挑出j双,每双筷子差的平方的和的最小值
然后将筷子长度从小到大排序.因为给出的筷子长度是乱的,如果排序能保证每次选取邻近两根的筷子比选取不临近的两跟筷子的差要小,不排序则要跳着选,dp方程不好写
接着初始化dp数组,再枚举i,j,若不选第i根,dp[i][j]=dp[i-1][j],若选择,则需要再枚举一个m和i配对,dp[i][j]=min(dp[i][j],dp[m-1][j-1]+(t[i]-t[m])²)
代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
int n,k;
int dp[101][61];//dp[i][j]表示前i个中选出j双的差平方最小值
const int inf=1<<30;
int t[101];
int main() {
cin >> n >> k;
k+=3;
for(int i=1;i<=n;i++) cin >> t[i];
if(k*2>n) {
cout << -1 << endl;
return 0;
}
sort(t+1,t+n+1);//排序是为了保证选择相邻的一双筷子差值的平方最小
fill(dp[0],dp[0]+101*61,inf);
for(int i=0;i<=n;i++) dp[i][0]=0;//不组成一对
for(int i=1;i<=n;i++) {
for(int j=1;j<=k;j++) {//j双筷子
dp[i][j]=dp[i-1][j];//第I根不选
for(int m=1;m<i;m++) {//选另外一个并组成差平方最小的
dp[i][j]=min(dp[i][j],dp[m-1][j-1]+(t[i]-t[m])*(t[i]-t[m]));
}
}
}
cout << dp[n][k] << endl;
return 0;
}
2.不重叠线段(传送门:http://www.caioj.cn/problem.php?id=1078)
先定义一个数组dp[i]表示覆盖区间[1,i]最大覆盖数字的数量
然后将线段的起点从小到大排序,方便后面dp
枚举每条线段,将这条线段的终点对应的覆盖数与这条线段起点前对应的覆盖数加上线段长度作比较,若终点覆盖数更小则更新到较大值
然后将这条线段后的所有端点对应的覆盖数更新为这个最大值
代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n;
struct node {
int x,y;
};
vector<node> v;
int dp[2002]={0};//dp[i]使用某些线段,覆盖区间[1,n],最多可以覆盖多少个数字
bool cmp(node a,node b) {
return a.x<b.x;
}
int main() {
cin >> n;
for(int i=1;i<=n;i++) {
int a,b;
cin >> a >> b;
a++,b++;
if(a>b) swap(a,b);//若线段是反的就挑头
v.push_back((node){a,b});
}
sort(v.begin(),v.end(),cmp);//将每条线段按起点值从小到大排序,方便后面dp
for(int i=0;i<v.size();i++) {
if(dp[v[i].x-1]+(v[i].y-v[i].x+1)>dp[v[i].y]) {
dp[v[i].y]=dp[v[i].x-1]+(v[i].y-v[i].x+1);//若这条线段前已经覆盖的个数加上线段覆盖长度大于当前的这条线段覆盖个数,更新当前值
for(int j=v[i].y+1;j<=2001;j++) {//更新后面位置的最大覆盖值
dp[j]=max(dp[j],dp[v[i].y]);
if(dp[j]>dp[v[i].y]) break;//更新完毕
}
}
}
cout << dp[2001] << endl;
return 0;
}