题目整理-2

T1Sue 的小球

题面描述:

给你一个平面直角坐标系,你初始在 \((x_0,0)\) 的位置。在这个坐标系上,有 \(n\) 个彩蛋位于这个坐标系上,第 \(i\) 个彩蛋的坐标为 \((x_i,y_i)\),每个时刻下落的高度为 \(v_i\ (v_i>0)\)。每个时刻可以向左或向右移动一个单位长度并收集这个位置上空(或下面)的彩蛋,得到的分数为当前这个彩蛋的千分之一。问你在收集所有彩蛋的前提下,最大获得的分数为多少?

思路:

首先,我们可以发现,当我们走到一个彩蛋的下方时,立刻收集这个彩蛋一定是最优的,所以,收集到的彩蛋一定是在连续的一段区间内的。所以,当我们收集完彩蛋后,一定在这个已收集彩蛋区间的左边或右边。因此,设 \(f_{i,j,k}\ (k=1\ /\ 0)\) 表示在 \([i,j]\) 中,收集所有彩蛋后的最大值,并且位于区间的左侧(或右侧)处。因为这次的操作会影响到后面彩蛋的下落,所以提前将为收集到的彩蛋下落的高度算入进去即可。

标签:

区间dp
费用提前计算

代码实现

this
#include<bits/stdc++.h>
#define INF ((int)0xc0c0c0c0)
#define pr3 pair<pair<int,int>,int>
#define fr first.first
#define se first.second
#define th second
#define N 1005
using namespace std;
int n,tot,x0,x[N],y[N],v[N];
int f[N][N][2];
pr3 a[N];
int main(){
	memset(f,0xc0,sizeof(f));
	cin>>n>>x0;
	for(int i=1;i<=n;i++) cin>>a[i].fr;
	for(int i=1;i<=n;i++) cin>>a[i].se;
	for(int i=1;i<=n;i++) cin>>a[i].th;
	a[++n].fr=x0;
	sort(a+1,a+n+1);//一定要排序!!!
	x[tot]=INF;
	for(int i=1;i<=n;i++){
		if(x[tot]!=a[i].fr) ++tot,v[tot]=v[tot-1];
		x[tot]=a[i].fr,y[tot]+=a[i].se,v[tot]+=a[i].th;
		if(x[tot]==x0) f[tot][tot][0]=f[tot][tot][1]=y[tot];
	}
	n=tot;
	for(int len=1;len<n;len++){
		for(int i=1;i<=n-len+1;i++){
			int j=i+len-1;
			if(i>1) f[i-1][j][0]=max(f[i-1][j][0],f[i][j][0]+y[i-1]-(x[i]-x[i-1])*(v[i-1]+v[n]-v[j]));
			if(j<n) f[i][j+1][1]=max(f[i][j+1][1],f[i][j][0]+y[j+1]-(x[j+1]-x[i])*(v[i-1]+v[n]-v[j]));
			if(i>1) f[i-1][j][0]=max(f[i-1][j][0],f[i][j][1]+y[i-1]-(x[j]-x[i-1])*(v[i-1]+v[n]-v[j]));
			if(j<n) f[i][j+1][1]=max(f[i][j+1][1],f[i][j][1]+y[j+1]-(x[j+1]-x[j])*(v[i-1]+v[n]-v[j]));
		}
	}
	printf("%.3lf\n",max(f[1][n][0],f[1][n][1])*0.001);
	return 0;
}

T2Replace on Segment

题面描述:

给你一个长度为 \(n\) 的序列 \(a\) ,保证所有的 \(a_i\)\([1,x]\) 中。每次可选择一个区间,将该区间内的所有数字变为区间内不存在的一个数且该数必须在 \([1,x]\) 中,问将这个长度为 \(n\) 的序列变为同一个数的最小操作次数。

思路:

\(dp\) !
\(f_{i,j,k}\) 表示将区间 \([i,j]\) 全部变为 \(k\) 的操作最小次数,则有两种转移:

  1. 从比它小的区间转移。
  2. 从同区间但不同 \(k\) 的区间转移。

两种转移对应的方程如下:

  1. \(f_{i,j,k}=f_{i,h,k}+f_{h+1,j,k}\ (i\leq h<j)\)
  2. \(f_{i,j,k}=f_{i,j,h}+1\ (k\ne h)\)

但是,第一个方程明显有一个小问题,若左右两边都是将整个大区间同时覆盖为 \(k\) ,则会有一次多余的操作要减去,所以,我们直接设 \(g_{i,j,k}\) 表示在 \(f_{i,j,k}\) 为最优的情况下,是否可以使最后一次操作为将区间 \([i,j]\) 变为 \(k\) ,贪心得,在 \(f_{i,j,k}\) 值相同的情况下,\(g_{i,j,k}\)\(1\) 时更优。

标签:

区间dp

代码实现

this
#include<bits/stdc++.h>
#define N 105
#define INF 0x3f3f3f3f
using namespace std;
int T,n,m,a[N],f[N][N][N],g[N][N][N],ans;
int main(){
	cin>>T;
	while(T--){
		cin>>n>>m;
		memset(f,0x3f,sizeof(f));
		memset(g,0,sizeof(g));
		ans=(n==1)?0:INF;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			for(int k=1;k<=m;k++) f[i][i][k]=(k!=a[i]),g[i][i][k]=(k!=a[i]);
		}
		for(int len=2;len<=n;len++){
			for(int i=1;i<=n-len+1;i++){
				int j=i+len-1,mn=0;
				for(int k=1;k<=m;k++){
					for(int h=i;h<j;h++){
						if(f[i][j][k]>f[i][h][k]+f[h+1][j][k]-(g[i][h][k]&g[h+1][j][k])){
							g[i][j][k]=g[i][h][k]&g[h+1][j][k];
							f[i][j][k]=f[i][h][k]+f[h+1][j][k]-(g[i][h][k]&g[h+1][j][k]);//第一个
						}
						else if(f[i][j][k]==f[i][h][k]+f[h+1][j][k]-(g[i][h][k]&g[h+1][j][k])&&(g[i][h][k]&g[h+1][j][k]))
							g[i][j][k]=1;
					}
					if(i==1&&j==n) ans=min(ans,f[i][j][k]);
					if(f[i][j][k]<f[i][j][mn]) mn=k;
				}
				for(int k=1;k<=m;k++){
					if(f[i][j][k]!=f[i][j][mn]) f[i][j][k]=f[i][j][mn]+1,g[i][j][k]=1;//第二个
				}
			}
		}
		cout<<ans<<"\n";
	}
	return 0;
}

注:

  1. 题号为本校OJ上的链接,题名为原出处链接。

$$The\ end$$

posted @ 2025-02-23 21:12  skx_515  阅读(11)  评论(0)    收藏  举报