难度:普及(csp入门)
csp 赛前模拟赛1
题面
T 1 M e s s a g e R e l a y T_1\ Message\ Relay T1 Message Relay
解析
T
1
T_1
T1数据小,直接爆搜 (正解链式前向星
代码
#include<bits/stdc++.h>
using namespace std;
int N,a[1039],f[1039],ans;
bool q[1039];
bool find(bool s[],int n) {
if(a[n]==1)return 1;
if(s[n]||a[n]==-1)return 0;
s[n]=1;
return find(s,f[n]);
}
int main() {
freopen("relay.in","r",stdin);
freopen("relay.out","w",stdout);
scanf("%d",&N);
for(int i=1; i<=N; i++) {
scanf("%d",&f[i]);
if(f[i]==0) {
ans++,a[i]=1;
}
}
for(int i=1; i<=N; i++) {
if(f[i]!=0) {
memset(q,0,sizeof(q));
if(find(q,i))ans++,a[i]=1;
else a[i]=-1;
}
}
printf("%d\n",ans);
}
T 2 01 数 字 排 序 T_2\ 01数字排序 T2 01数字排序
暂时未切,有DL会的话评论区见
T 3 P o k e r H a n d s T_3\ Poker\ Hands T3 Poker Hands
暴力只有10分
思路
模拟一下数据
in 15 12 16 2 0 5 15 19
out 38
手从一个数开始推:
in | out | 变化 | 解析 |
---|---|---|---|
15 | 15 | / | / |
15 12 | 15 | / | / |
15 12 16 | 19 | +4 | 第一个上升的点(12->16) |
15 12 16 2 | 19 | / | / |
15 12 16 2 0 | 19 | / | / |
15 12 16 2 0 5 | 24 | +5 | 再次上升(0->5) |
15 12 16 2 0 5 15 | 34 | +10 | 同样上升(5->15) |
15 12 16 2 0 5 15 19 | 38 | +4 | 仍上升(15->19) |
可以发现是个线性DP
- 上升的时候, a n s ans ans是上升的幅度
- 下降时不变
代码
#include<bits/stdc++.h>
using namespace std;
long long N,a,an,ans,f;
bool y;
int main() {
freopen("poker.in","r",stdin);
freopen("poker.out","w",stdout);
scanf("%d",&N);
for(long long i=1; i<=N; i++){
scanf("%lld",&a);
if(f<a)ans+=a-f;
f=a;
}//f自己滚自己
printf("%lld",ans);
return 0;
}
T 4 T h e C o w R u n T_4\ The\ Cow\ Run T4 The Cow Run
解析
一个有着无数细节的区间DP
DP状态设计
f
i
j
f_{i}{}_{j}
fij为第
i
i
i个数到第
j
j
j个数中间区间的最优值
然后因为要知道当前状态是从左边过来的还是右边过来的,所以要加一个0和1
DP递推式
D P = { f i j 0 = min ( f i + 1 j 0 + n u m i j ∗ s i i + 1 , f i + 1 j 1 + n u m i j ∗ s i j ) f i j 1 = min ( f i j − 1 0 + n u m i j ∗ s i j , f i j − 1 1 + n u m i j ∗ s j − 1 j ) s i j = ∣ p i − p j ∣ n u m i j = n − j + i + 1 DP=\left\{ \begin{aligned} {f}_{i\ j\ 0} &= \min(f_{i+1\ j\ 0}+num_{i\ j}*s_{i\ i+1},f_{i+1\ j\ 1}+num_{i\ j}*s_{i\ j})\\ {f}_{i\ j\ 1} &=\min(f_{i\ j-1\ 0}+num_{i\ j}*s_{i\ j},f_{i\ j-1\ 1}+num_{i\ j}*s_{j-1\ j})\\ s_{i\ j} &=|p_i-p_j|\\ num_{i\ j}&= n-j+i+1\\ \end{aligned} \right. DP=⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧fi j 0fi j 1si jnumi j=min(fi+1 j 0+numi j∗si i+1,fi+1 j 1+numi j∗si j)=min(fi j−1 0+numi j∗si j,fi j−1 1+numi j∗sj−1 j)=∣pi−pj∣=n−j+i+1
无数的细节
- 初始值:在 f i j 1 / 0 f_{i\ j\ 1/0} fi j 1/0中 i > j i>j i>j是无意义的
- 无效区间:当 i > ( 原 点 在 的 位 置 ) i>(原点在的位置) i>(原点在的位置)或 j < ( 原 点 在 的 位 置 ) j<(原点在的位置) j<(原点在的位置)时,区间无意)
- 枚举方向: i q → 0 i\ q\to0 i q→0 \ {} j q → n j\ q\to n j q→n (q为原点在的位置)
代码
#include<bits/stdc++.h>
using namespace std;
int N,a[1039],f[1039][1039][2],an,ans,s[1039][1039],q,Min=10000000;
int main() {
freopen("cowrun.in","r",stdin);
freopen("cowrun.out","w",stdout);
scanf("%d",&N);
for(int i=1; i<=N; i++) {
scanf("%d",&a[i]);
}memset(f,0x3f,sizeof(f));
sort(a,a+1+N);
for(int i=0;i<=N;i++){
if(a[i]==0)q=i;
for(int j=0;j<=N;j++){
s[j][i]=abs(a[i]-a[j]);
}
}
f[q][q][0]=f[q][q][1]=0;
for(int i=q;i>=0;i--){
for(int j=q;j<=N;j++){
if(i==j)continue;
f[i][j][0]=min(f[i+1][j][0]+(N-j+i+1)*s[i][i+1],f[i+1][j][1]+(N-j+i+1)*s[i][j]);
f[i][j][1]=min(f[i][j-1][0]+(N-j+i+1)*s[i][j],f[i][j-1][1]+(N-j+i+1)*s[j-1][j]);
}
}
printf("%d\n",min(f[0][N][1],f[0][N][0]));
return 0;
}