区间dp例题

1. ICPC Beijing 2017 J, Pangu and Stones

In Chinese mythology, Pangu is the first living being and the creator of the sky and the earth. He woke up from an egg and split the egg into two parts: the sky and the earth.

At the beginning, there was no mountain on the earth, only stones all over the land.

There were N piles of stones, numbered from 1 to N. Pangu wanted to merge all of them into one pile to build a great mountain. If the sum of stones of some piles was S, Pangu would need S seconds to pile them into one pile, and there would be S stones in the new pile.

Unfortunately, every time Pangu could only merge successive piles into one pile. And the number of piles he merged shouldn’t be less than L or greater than R.

Pangu wanted to finish this as soon as possible.

Can you help him? If there was no solution, you should answer 0.

Input
There are multiple test cases.

The first line of each case contains three integers N,L,R as above mentioned (2≤N≤100,2≤L≤R≤N).

The second line of each case contains N integers a 1 , a 2 , … , a N ( 1 ≤ a i ≤ 1000 , i = 1 … N ) a_1,a_2,…,a_N(1≤a_i≤1000,i=1…N) a1,a2,,aN(1ai1000,i=1N), indicating the number of stones of pile 1, pile 2, …, pile N.

The number of test cases is less than 110 and there are at most 5 test cases in which N≥50.

Output
For each test case, you should output the minimum time(in seconds) Pangu had to take. If it was impossible for Pangu to do his job, you should output 0.

Example
input

3 2 2
1 2 3
3 2 3
1 2 3
4 3 3
1 2 3 4

output

9
6
0
题目大意

有n堆石头,要求合并成一堆,并且每次合并的堆数在L到R之间,输出最小次数,不能合并输出0;

思路

因为不是两堆合成一堆,是要多堆合成一堆,所以我们不能单纯的两个合一个
我们要多开一个状态表示此时的堆数,看看是几堆合成一个才是最小
f [l] [r] [k] = min ( f[l] [r] [k] ,f[l] [mid] [1] + f [mid+1] [r] [mid-1] );
状态转移时还是分成两部分。 合成一个 f [l] [r] [1] = min ( f[l] [r] [1] ,f [l] [r] [k] )+ s [r] -s [l-1]
找l,r中怎么分才是最大。然后加上代价

#include<iostream>

using namespace std;
const int N=111;
/* 因为不是两堆合成一堆,是要多堆合成一堆,所以我们不能单纯的两个合一个.
我们要多开一个状态表示此时的堆数,看看是几堆合成一个  f[l][r][k]=min(f[l][r][k],f[l][mid][1]+f[mid+1][r][mid-1]); 
状态转移时还是分成两部分。  合成一个 f[l][r][1]=min(f[l][r][1],f[l][r][k])+s[r]-s[l-1]  
找l,r中怎么分才是最大。然后加上代价 
*/ 
int a[N],s[N],f[N][N][N]; // f[l][r][k]表示在 l到 r中的区间合并成k堆的消耗 
int n,L,R;
void init(){
	
   	for(int l=1;l<=n;l++)
	for(int r=l;r<=n;r++)
		for(int k=1;k<=n;k++)
			f[l][r][k]=0x3f3f3f3f;
}
void solve()
{
	for(int d=0;d<=n;d++)  //  枚举区间长度 
	{
		for(int l=1;l+d<=n;l++)  // 枚举区间 
		{
			int r=l+d;
			if(d==0) f[l][r][1]=0;  // 如果长度为0,说明只有一位数,就是他本身,没有耗损 
			else 
			{
				for(int k=2;k<=R;k++)  //  枚举堆数 
				{
					for(int mid=l;mid<r;mid++)   //  划分区间转移 
					{
						f[l][r][k]=min(f[l][r][k],f[l][mid][1]+f[mid+1][r][k-1]); 
						if(k>=L)f[l][r][1]=min(f[l][r][1],f[l][r][k]);
					}
				}f[l][r][1]+=s[r]-s[l-1];  //  加上耗损 
			}
		}
	}
	if(f[1][n][1]>=0x3f3f3f3f) cout<<"0"<<endl;
	else cout<<f[1][n][1]<<endl;
}
int main()
{
	while(cin>>n>>L>>R)
	{
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
			s[i]=s[i-1]+a[i];
		}
		
		init();
		solve(); 
	
	}
	return 0;
 } 

2. ICPC Kunming 2020 C, Cities

Bob lives in a chaotic country with n cities in a row, numbered from 1 to n. These cities are owned by different lords, and the i-th cities currently belongs to the ai-th lord. To simply problems, we assume there are n lords in the country, and they are also numbered from 1 to n. Some lords may take control of multiple cities, while some new-born lords have not got any cities yet.

Obviously, the greedy lords are not satisfied with the number of territories they have, so the country is constantly at war. Bob wants to change that, by making all the cities belong to the same lord!

Bob can perform some magical operations to support his grand plan. With the help of each magic, Bob can do the following:

Choose some cities with consecutive indices such that they belong to the same lord, and assign them to any other lord.
As magics are really tiring, Bob wants to know the minimum number of such operations he needs to use to make all the cities belong to one lord.

Input
The first line contains a single integer t(1≤t≤160) - the number of test cases.

The first line of each test case contains an integers n(1≤n≤5000) - the number of cities in the country.

The second line of each test case contains n integers ai(1≤ai≤n) - the i-th city was originally owned by the ai-th lord. It is guaranteed that currently no Lord will have more than 15 cities, which means no one ai will appear more than 15 times in this line.

It is guaranteed that the sum of n over all test cases doesn’t exceed 6000.

Output
For each test case, print a single integer indicating the answer.

Example
input

2
8
4 3 1 2 1 1 3 3
5
1 2 3 2 1

output

3
2
题目大意

有n个城市 每个数字代表一个领主,我们合并连续的一些城市,让他变成同一个领主,问所有合并成一个领主最少几次

思路

有点像洛谷的涂色 但是洛谷数据范围小,暴力即可。
此题范围较大,我们每次不能遍历区间,需要优化。但是本题保证相同的数不会超过15个,那么我们就只遍历和在L到R内,和首区间相同的点为划分点
时间复杂度 O ( n × n × 15 ) O(n\times n \times 15) O(n×n×15)

此题答案就是 n-1 - 最大的首尾相等的区间内有几个首尾相等的区间
假如一个首尾相等的区间里的数都是不同的,我们要一个个把它变相同(和区间首位相同)
最后把这个区间变相等的操作是一次,而不是首尾都要变的两次(少一次)
所以我们统计有几个这样的首尾相同区间 ,总数减去这个次数就是答案 但是最后一个区间是区间中的数向区间首尾看齐,
所以最后一个区间是不用减的(少两次)

#include<iostream>

using namespace std;
const int N=5e3+10;
/*  答案就是 n-1 - 最大的首尾相等的区间内有几个首尾相等的区间 
假如一个首尾相等的区间里的数都是不同的,我们要一个个把它变相同(和区间首位相同) 
最后把这个区间变相等的操作是一次,而不是首尾都要变的两次(少一次)
所以我们统计有几个这样的首尾相同区间 ,总数减去这个次数就是答案 但是最后一个区间是区间中的数向区间首尾看齐,
所以最后一个区间是不用减的(少两次) 
*/ 
int f[N][N],a[N];   //  f[l][r] 就是l到r中 有几个首尾相等的区间 
int t,n;
int pos[N],ne[N];
void init()
{
	cin>>n;

	for(int i=1;i<=n;i++) 
	{
		pos[i]=n+1;	
	} 
	
	for(int i=1;i<=n;i++)  //  去掉连续的重复城市(看成一个即可)
	{
		cin>>a[i];
		if(i!=1 && a[i]==a[i-1]) i--,n--;
	}
	
	for(int i=n;i>=1;i--) //  记录相同的数下一个在哪里。 
	{
		ne[i]=pos[a[i]];
		pos[a[i]]=i;
	}
}
void solve()
{
	for(int d=0;d<=n;d++)
	{
		for(int l=1;l+d<=n;l++)
		{
			int r=l+d;
			f[l][r]=0;  //  初始化 
			f[l][r]=f[l+1][r];
			int x=ne[l];
			while(x<=r)
			{
				f[l][r]=max(f[l][r],f[l+1][x-1]+f[x][r]+1);//因为下一个相同的数是和下一个区间连着的,所以是~x-1,x~ 
				x=ne[x];
			}
			//cout<<l<<" "<<r<<" "<<f[l][r]<<endl;
		}	
	} 
	cout<<n-1-f[1][n]<<endl;
}
int main()
{
	cin>>t;
	while(t--)
	{
		init();
		solve();
	}
	
	return 0;
}

3. ICPC CERC 2014 L, Outer space invaders

The aliens from outer space have (finally!) invaded Earth. Defend yourself, or be disintegrated! Or assimilated. Or eaten. We are not yet sure.

The aliens follow a known attack pattern. There are n attackers, the i-th one appears at time ai, at distance di from you. He must be destroyed no later than at time bi, or else he will fire his weapon, which will definitely end the fight.

Your weapon is an area-blaster, which can be set to any given power. If fired with power R, it momentarily destroys all aliens at distance R or smaller. It also consumes R fuel cells.

Determine the minimal cost (measured in fuel cells) of destroying all the aliens, without being killed.

Input
The first line of input contains the number of test cases T. The descriptions of the test cases follow:

Each test case starts with a line containing the number of aliens n(1≤n≤300). Of the next n lines, the i-th one contains three integers ai,bi,di(1≤ai<bi≤10000,1≤di≤10000)
The i-th alien appears at time ai, is idle until bi, and his distance from you is di.

注:原题面里没有写T的范围,可以大概认为如果数据中都n=300,那么T≤10。

Output
For each test case, output one line containing the minimum number of cells needed to destroy all the aliens.

Example
input

1
3
1 4 4
4 7 5
3 4 7

output

7
题目大意

外星人入侵,你有一个范围性武器,可以消耗R能力消灭R范围的敌人,给出敌人出现的初始时间,消失时间和地点。求消灭所有敌人消耗最少能量。

思路

在这里插入图片描述
记忆化搜索
我们找一个区间里的最大高度,取最大高度的时间内一点x进行轰炸,所有穿过x的敌人都会死亡,耗费就是最大高度,然后可以再划分成[l,x-1] 和 [x+1,r] 两个区间,再进行这样的操作

#include<bits/stdc++.h> 

using namespace std;
const int N=633,inf=1<<29;
int a[N],b[N],d[N],dp[N][N];
int t,n,m;
int solve(int l,int r)  //  记忆化区间dp 
{
	if(l>r) return 0;  // 处理边界 
	if(dp[l][r]!=-1) return dp[l][r];  //  记忆化 
	
	int &ans=dp[l][r];
	ans=inf;  //  初始化 
	
	int maxd=-1,mxpos=-1;  //  记录最大距离的点 
	for(int i=1;i<=n;i++) 
		if(a[i]>=l && b[i]<=r)
		{
			if(maxd<d[i]) maxd=d[i],mxpos=i;
		}
	
	if(maxd==-1) return ans=0;   
	for(int i=a[mxpos];i<=b[mxpos];i++)  // 以该区间的每个点划分,找最小的组合 
	{
		ans=min(ans,solve(l,i-1)+solve(i+1,r));
	}
	ans+=maxd;
	return ans;
}
void solve()
{
	cin>>n; 
	vector<int> v;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i]>>b[i]>>d[i];
		v.push_back(a[i]);
		v.push_back(b[i]);
	}
	
	//  离散化 
	sort(v.begin(),v.end());
	v.erase(unique(v.begin(),v.end()),v.end());
	for(int i=1;i<=n;i++)
	{
		a[i]=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1;
		b[i]=lower_bound(v.begin(),v.end(),b[i])-v.begin()+1;
	}
	//  初始化 
	m=v.size();
	for(int i=1;i<=m;i++)
	{
		for(int j=i;j<=m;j++) dp[i][j]=-1;
	}
	
	printf("%d\n",solve(1,m)); 
}
int main()
{
	cin>>t;
	while(t--)  solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值