题目整理-2
T1、Sue 的小球
题面描述:
给你一个平面直角坐标系,你初始在 \((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;
}
T2、Replace on Segment
题面描述:
给你一个长度为 \(n\) 的序列 \(a\) ,保证所有的 \(a_i\) 在 \([1,x]\) 中。每次可选择一个区间,将该区间内的所有数字变为区间内不存在的一个数且该数必须在 \([1,x]\) 中,问将这个长度为 \(n\) 的序列变为同一个数的最小操作次数。
思路:
上 \(dp\) !
设 \(f_{i,j,k}\) 表示将区间 \([i,j]\) 全部变为 \(k\) 的操作最小次数,则有两种转移:
- 从比它小的区间转移。
- 从同区间但不同 \(k\) 的区间转移。
两种转移对应的方程如下:
- \(f_{i,j,k}=f_{i,h,k}+f_{h+1,j,k}\ (i\leq h<j)\)
- \(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;
}
注:
- 题号为本校OJ上的链接,题名为原出处链接。

浙公网安备 33010602011771号