CSP2019 J2参考解析
CSP2019 J2
题目传送
P5660 [CSP-J2019] 数字游戏
- 涉及知识点:基础语法
- 解析:直接堆1的个数进行计数即可
废话,简单题,没说的
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int ans=0;
string s; cin>>s;
for(int i=0; i<s.size(); i++){
if(s[i]=='1') ans++;
}
cout<<ans<<endl;
return 0;
}
P5661 [CSP-J2019] 公交换乘
-
涉及知识点:模拟
-
解析:也是一个模拟题,可以直接暴力模拟
-
如果是 subway, money+=price.
-
如果是 bus,看有没有免费票
-
免费票的查询可以每次从第一次行程开始,但是这样复杂度为 O(n^2)会超时。
-
所以可以根据时间限制 45 分钟进行优化,开一个pre记录当前最早可用票的位置,这样复杂度整体可以维护在 O(n)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e6+10;
int n,op[N],t[N],price[N],pre=1;
LL money=0;
int main(){
// freopen("data.in", "r", stdin);
cin>>n;
for(int i=1; i<=n; i++) cin>>op[i]>>price[i]>>t[i];
for(int i=1; i<=n; i++){
if(op[i]==0){ // subway
money += price[i];
}else if(op[i]==1){// bus
while(t[i]-t[pre] > 45) pre++;//时间优化
int flag=0,p=pre;
while(p<i){ // 找免费票
if(op[p]==0&&t[i]-t[p]<=45&&price[i]<=price[p]){
t[p]=-50, flag=1; break;
}
p++;
}
if(flag==0) money+=price[i];
}
}
cout<<money<<endl;
return 0;
}
P5662 [CSP-J2019] 纪念品
-
涉及知识点:背包问题
-
解析:本题的数据有一定的特性,很适合拿部分分,我们将重特殊数据入手,带大家看一下,在无法想到正解情况下尽力拿分。
-
对于 10% 的数据,T=1。
-
试想一下,如果 T=1,意味着什么?
-
就只有一天的交易时间,我们无论是买或不买,本质上金币的数量不可能有增长。
-
所以这就是出题人的送分。
if(t==1) cout<<m; // 10分到手
- 再来看下一个
- 对于 15% 的数据,T≤100,N=1。
- 如果 N=1,那么也就是只有一个物品的时候,这时候的买卖其实就比较简单了
- 就拿样例1 举例
6 1 100
50 20 25 20 25 50
- 我们可以将 每个递增区间看作一个买卖区间,L天买入,R天卖出,利用双指针就可以完成了。
if(n==1) {
int l=1,r=1;
while(r<t) {
while(r<t && p[r+1][1]>=p[r][1]) r++;
if(r>t) break; // l 买入,r 卖出
m += m/p[l][1] * (p[r][1]-p[l][1]);
l = ++r; // 更新一个买卖阶段
}
}
cout<<m; // 15 分到手
- 当然这个题目的正解其实可以对问题进行转化得到
- 买卖区间 [L,R],,L天买入,R天卖出
- 等同于 [L,L+1] + [L+1,L+2] + [L+2,L+3] + ... +[R-1, R]。
- 问题就可以转化为:求在第 2,3,4,...,t 天能获得的最多金币。
- 由于每次买卖间隔不超过一天,所以需要最大化每天的金币数量
- 这样就变成了完全背包问题,进行 (t-1)次完全背包即可。
- 一次背包问题
- 定义状态:f[i] 背包容量为 i时的最大价值
- 状态转移:f[i]=max(f[i], f[j-w]+v)
- 目标答案:f[m]
- 其中:代价为 p[i][j],价值为每天交易的获利 p[i+1][j]-p[i][j]。
- 另外还可以加一个优化: if(p[i+1][j] > p[i][j]) // 优化:有价值才用
for(int i=1; i<t; i++) {
memset(f, 0, sizeof(f));
for(int j=1; j<=n; j++) {
if(p[i+1][j] > p[i][j]) // 优化:有价值才用
for(int k=p[i][j]; k<=m; k++) {
f[k] = max(f[k], f[k-p[i][j]]+p[i+1][j]-p[i][j]);
}
}
m += f[m];
}
cout<<m;
- 其实考场上写如下程序是最为保险的,因为有时候自己也不一定确定正解是否是正解。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=110, M=1e4+10,INF=0x3f3f3f3f;
int t,n,m,p[N][N],f[M];
int main() {
// freopen("data.in", "r", stdin);
cin>>t>>n>>m;
for(int i=1; i<=t; i++)
for(int j=1; j<=n; j++) cin>>p[i][j];
if(t==1) {
// cout<<m;
} else if(n==1) {
int l=1,r=1;
while(r<t) {
while(r<t && p[r+1][1]>=p[r][1]) r++;
if(r>t) break; // l 买入,r 卖出
m += m/p[l][1] * (p[r][1]-p[l][1]);
l = ++r; // 更新一个买卖阶段
}
} else { // 正解:完全背包
for(int i=1; i<t; i++) {
memset(f, 0, sizeof(f));
for(int j=1; j<=n; j++) {
if(p[i+1][j] > p[i][j]) // 优化:有价值才用
for(int k=p[i][j]; k<=m; k++) {
f[k] = max(f[k], f[k-p[i][j]]+p[i+1][j]-p[i][j]);
}
}
m += f[m];
}
}
cout<<m<<endl;
return 0;
}
P5663 [CSP-J2019] 加工零件
- 涉及知识点:最短路
- 解析:题目需要进行转化
- 转化为:问是否存在【从 1->a 的最短路 dis[a] <= L,且二者同奇偶性】的情况。
- 同奇偶性是指:L,L+2,L+4,L+6,... ,这样的性质。
- 这时候其实就是求单源最短路了,可以使用 spfa-其实就是bfs,dijkstra。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10,INF=0x3f3f3f3f;
int n,m,q,head[N],cnt=0,dis[N][2];
struct T {
int to,nxt;
T(int a=0,int b=0):to(a),nxt(b) {}
} G[N<<1], que[N<<1], p;
void add(int u,int v) {
G[++cnt].to=v, G[cnt].nxt=head[u], head[u]=cnt;
}
void bfs() {
memset(dis, 0x3f, sizeof(dis));
int front=0, tail=-1;
dis[1][0]=0,que[++tail] = T(1,0);
while(front<=tail) {
p = que[front++];
int u=p.to, type=p.nxt, dist=dis[u][type];
for(int i=head[u]; ~i; i=G[i].nxt) {
int v=G[i].to;
if(dis[v][type^1] > dist+1) {
dis[v][type^1] = dist+1;
que[++tail] = T(v,type^1);
}
}
}
}
int main() {
// freopen("data.in", "r", stdin);
ios::sync_with_stdio(0);
memset(head, -1, sizeof(head));
cin>>n>>m>>q; int u,v,a,l;
for(int i=1; i<=m; i++) {
cin>>u>>v;
add(u,v), add(v,u);
}
bfs();
while(q--) {
cin>>a>>l;
if(head[1]==-1) puts("No");
else if(l>=dis[a][l&1]) puts("Yes");
else puts("No");
}
return 0;
}