JOI 精选
【未完稿】
距 CSP-J/S 2026 第二轮 还剩 33 天。
P4138 [JOISC 2014] 挂饰 / Straps
JOI 君有 \(N\) 个装在手机上的挂饰,编号为 \(1,2,\cdots,N\)。JOI 君可以将其中的一些装在手机上。
JOI 君的挂饰有一些与众不同——其中的一些挂饰附有可以挂其他挂件的挂钩。每个挂件要么直接挂在手机上,要么挂在其他挂件的挂钩上。直接挂在手机上的挂件最多有 \(1\) 个。
此外,每个挂件有一个安装时会获得的喜悦值,用一个整数来表示。如果 JOI 君很讨厌某个挂饰,那么这个挂饰的喜悦值就是一个负数。
JOI 君想要最大化所有挂饰的喜悦值之和。注意不必要将所有的挂钩都挂上挂饰,而且一个都不挂也是可以的。
\(1\leq N\leq 2000\)。
\(0\leq A_i\leq N(1\leq i\leq N)\)。
\(-10^6\leq B_i\leq 10^6(1\leq i\leq N)\)。
sol
观察发现,一个状态只和有多少个挂钩有关。
以及应当让挂钩少的挂在挂钩多的上,不然挂不上。
于是先把挂件按挂钩个数从大到小排序,\(dp_{i,j}\) 表示处理到第 \(i\) 个挂件时,有 \(j\) 个挂钩的喜悦值最大值。
转移容易,但记得 \(j\) 只需保存到 \(n\)(因为一共就 \(n\) 个挂件)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
struct edge{
int cnt,v1,v2;
}a[2005];
int dp[2005][2005];
bool cmp(edge x,edge y){
return x.v1>y.v1;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].v1>>a[i].v2;
a[i].cnt=i;
}
sort(a+1,a+n+1,cmp);
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
dp[i][j]=-1e18;
}
}
dp[0][1]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=n;j++){
dp[i][j]=dp[i-1][j];
}
for(int j=0;j<=n;j++){
if(dp[i-1][j]==-1e18) continue;
dp[i][(min(j-1+a[i].v1,n))]=max(dp[i][(min(j-1+a[i].v1,n))],dp[i-1][j]+a[i].v2);
}
}
int maxx=0;
for(int i=0;i<=n;i++){
maxx=max(maxx,dp[n][i]);
}
cout<<maxx;
return 0;
}
P6116 [JOI 2019 Final] 有趣的家庭菜园 3 / Growing Vegetables is Fun 3
家庭菜园专家 JOI 先生在他的家庭菜园中种植了一种叫 Joy 草的植物。在他的菜园里,有 \(N\) 个花盆自东向西摆放,第 \(i\) 个编号为 \(i\)。每个花盆中有一株 Joy 草。
春天到了,JOI 先生注意到 Joy 草如他期望地长出了各种颜色的叶子,但他也发现 Joy 草的生长速度没有他期望的那么快。他查阅了书籍,找到了草的以下特点:
-
Joy 草有三种品种,分别会长出红色、绿色和黄色的叶子。
-
如果两株同一颜色的 Joy 草紧密相邻,它们的生长速度就会减慢。
因此,JOI 先生决定重新摆放花盆,使得没有两株相邻的 Joy 草颜色相同。
花盆非常沉重,因此 JOI 先生每次只能交换相邻的两个花盆。形式化的说,JOI 先生每次操作可以选择一个 \(i\),然后交换花盆 \(i\) 和花盆 \(i+1\)。
请编写一个程序,计算最少的交换次数。
Subtask 1(5 分):\(N\le 15\)。
Subtask 2(55 分):\(N\le 60\)。
Subtask 3(15 分):字符串仅包含 R,G。
Subtask 4(25 分):\(N\le 400\)。
sol
首先,在初始序列和最终序列中,相同字母的相对顺序一定不变。
如果发生改变,那么会发生一次交叉,那么一定比最优差。
根据结论,确定最终序列,答案为逆序对数。
直接 \(dp\),考虑 \(dp_{i,j,k,0/1/2}\) 表示三种颜色 R,G,Y 分别有 \(i,j,k\) 个(依照顺序),以哪个颜色作为结尾。
转移时贡献为逆序对数。
设 \(g_{i,c}\) 表示原序列中第 \(i\) 个颜色 \(c\) 的位置,\(p_{x,c}\) 表示原序列前 \(x\) 个位置中颜色 \(c\) 的出现次数。
转移为:
其中 \(i>0\)。
其中 \(j>0\)。
其中 \(k>0\)。
答案为 \(\min(dp_{i,j,k,0},dp_{i,j,k,1},dp_{i,j,k,2})\)。
#include<bits/stdc++.h>
using namespace std;
int n;
string s;
int dp[405][405][405][3];
int p[405][3];
int g[405][3];
signed main(){
cin>>n;
cin>>s;
int sr=0,sg=0,sy=0;
for(int i=0;i<n;i++){
if(s[i]=='R') g[++sr][0]=i;
if(s[i]=='G') g[++sg][1]=i;
if(s[i]=='Y') g[++sy][2]=i;
}
for(int i=0;i<n;i++){
if(i==0) goto jj;
p[i][0]=p[i-1][0];
p[i][1]=p[i-1][1];
p[i][2]=p[i-1][2];
jj:;
if(s[i]=='R') p[i][0]++;
if(s[i]=='G') p[i][1]++;
if(s[i]=='Y') p[i][2]++;
}
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
for(int k=0;k<=n;k++){
dp[i][j][k][0]=dp[i][j][k][1]=dp[i][j][k][2]=1e9;
}
}
}
dp[0][0][0][0]=dp[0][0][0][1]=dp[0][0][0][2]=0;
for(int i=0;i<=sr;i++){
for(int j=0;j<=sg;j++){
for(int k=0;k<=sy;k++){
if(i>0) dp[i][j][k][0]=min(dp[i][j][k][0],min(dp[i-1][j][k][1],dp[i-1][j][k][2])+max(j-p[g[i][0]][1],0)+max(k-p[g[i][0]][2],0));
if(j>0) dp[i][j][k][1]=min(dp[i][j][k][1],min(dp[i][j-1][k][0],dp[i][j-1][k][2])+max(i-p[g[j][1]][0],0)+max(k-p[g[j][1]][2],0));
if(k>0) dp[i][j][k][2]=min(dp[i][j][k][2],min(dp[i][j][k-1][0],dp[i][j][k-1][1])+max(i-p[g[k][2]][0],0)+max(j-p[g[k][2]][1],0));
}
}
}
if(min({dp[sr][sg][sy][0],dp[sr][sg][sy][1],dp[sr][sg][sy][2]})>=1e9) cout<<-1;
else cout<<min({dp[sr][sg][sy][0],dp[sr][sg][sy][1],dp[sr][sg][sy][2]});
return 0;
}
OI / C++ / Algorithm
懒惰者出局。

浙公网安备 33010602011771号