gym 101908F Music Festival 背包DP

http://codeforces.com/gym/101908/problem/F

刚刚做了这个题,感觉有点套路,也有点烦。记一下解题的思路(其实是觉得这么水的题都做了这么久,有点过意不去)

 

题目大意:

给N组(N <= 10)有权值的区间,一共不超过1000个区间。在这些区间里选择若干不重叠(左右端点可以重)的区间,使得

1. N组里面每一组至少选一个区间

2.区间权值和最大

输出最大的权值和,如果找不到这样的区间集合,输出-1

 

解题思路:

离散化一下时间,一共有2000个时间点,记DP[i][j]表示在时间点 i ,状态为 j 的情况下能够取得的最大权值和。状态 j 是用N位2进制表示每一组是否选过,也就是说最多有1024种取值。

然后根据1000个区间作为物品来更新DP值。将区间按照左端点排序的话,每个区间能够更新DP[ 区间右端点 ][j],最多1024个值。这样可以在区间左端点之前的DP值里面找一个最大的来更新。这样需要查询区间的最大值,可以用1000个线段树来维护。

但是上面的做法我不想写。

于是枚举i,枚举j ,把DP[i][j]这个DP值取一个 “” 与 “状态 j 的历史最大值” 的最大值,加上左端点离 i 最近的区间更新后面的DP值即可,若有多个区间的左端点离 i 距离一样,就都更新一下。这样可以通过lower_bound来寻找位置。

也就是 DP[i][j] = max_val[j] = max(DP[i][j], max_val[j]) 以及 DP[nexti][nextj] = max(DP[nexti][nextj], DP[i][j] + val[k]),其中 k 为利用来更新的区间的编号。

 

AC代码:

#include 
using namespace std;
const int maxn = 3e3+10;
pair,pair > p[maxn];
int dp[3000][1300],f[4000],max_now[1300];
int cnt = 0,tot = 0;

//去重离散化,新的下标从1开始,最大下标为_cnt 
int idx[maxn],tmp_id[maxn],_cnt;
inline int get_id(int x){
	return lower_bound(idx,idx+_cnt,x) - idx; 
}
void discretize(int* f,int size){
	_cnt = 0; 
	for(int i = 0;i < size;i++) tmp_id[i] = f[i];
	sort(tmp_id,tmp_id+size);
	for(int i = 0;i < size;i++){
		idx[_cnt++] = tmp_id[i];
		while(i + 1 < size && tmp_id[i] == tmp_id[i+1]) i++;
	}
} 

int main(){
	ios::sync_with_stdio(false);
	int n,m,l;
	cin>>n;
	for(int i = 0;i < n;i++){
		cin>>m;
		for(int j = 0;j < m;j++){
			cin>>p[cnt].first.first>>p[cnt].first.second>>p[cnt].second.first;
			p[cnt].second.second = i;
			f[tot++] = p[cnt].first.first;f[tot++] = p[cnt].first.second;
			cnt++;
		}
	}
	sort(p,p+cnt);
	discretize(f,tot);
	for(int i = 0;i < cnt;i++){
		p[i].first.first = get_id(p[i].first.first);
		p[i].first.second = get_id(p[i].first.second);
	}
	memset(max_now,0x8f,sizeof(max_now));
	memset(dp,0x8f,sizeof(dp));
	dp[0][0] = max_now[0] = 0;
	for(int i = 0;i < tot;i++){
		for(int j = 0;j < (1<= 0) cout<

 

你可能感兴趣的:(动态规划)