202209
- 题目一:如此编码【100分】
- 题目二:何以包邮?【100分】
- 题目三:防疫大数据【100分】
题目一:如此编码【100分】
比较简单的题,根据题意计算一遍就行
一定要关注csp题目中的提示,这个是很有用的
AC代码:
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int N=25;
typedef long long LL;
int n;
LL m;
int a[N];
LL c[N];
int b[N];
int main() {
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
c[0]=1;
c[1]=a[1];
for(int i=2;i<=n;i++)
{
c[i]=c[i-1]*a[i];
}
for(int i=1;i<=n;i++)
{
LL t=m%c[i];
LL sum=0;
for(int j=0;j<i-1;j++)
{
sum+=c[j]*b[j+1];
}
t-=sum;
b[i]=t/c[i-1];
cout<<b[i]<<" ";
}
return 0;
}
题目二:何以包邮?【100分】
比赛的时候,就感觉跟0/1背包很像,就一直朝着那个方向去做,但是我dp太差了,当时的环境下怎么都想不出来,最后想着算了,枚举吧,得70分也挺好的,就用回溯简单枚举了一下
回溯枚举,,得到的是一个二叉树,时间复杂度为O(2^n)
当n<=15时,为O(2^15)=32768 ,1s内能过
当n<=30是,为O(2^30)=1073741824>1e8 ,肯定超时
70分回溯枚举代码:
#include<bits/stdc++.h>
using namespace std;
const int N=35;
int INF=0x3f3f3f3f;
int a[N],n,x,ans=INF,now;
void back(int i)
{
if(i>n)
{
if(now<x) return ;
if(now<ans) ans=now;
return ;
}
//选
now+=a[i]; back(i+1);
//不选
now-=a[i];back(i+1);
}
int main() {
cin>>n>>x;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
back(1);
cout<<ans;
return 0;
}
但其实这个回溯是可以优化来混点分的,加点剪枝什么的
然后再看dp背包方法,寻找大于等于包邮条件x的最小花费,其实发过来看,就是用总买的书的价格减去满足包邮条件x得到v,得到不超过v的最大花费,最后再用总价格减去这个不超过v的最大花费,就得到了结果,后者其实就是规范的0/1背包问题
AC代码:
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int N=35;
const int M=1000005;
int a[N];
int n,x,sum=0,dp[N][M];
int main() {
cin>>n>>x;
for(int i=1;i<=n;i++)
{
cin>>a[i];sum+=a[i];
}
int v=sum-x;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=v;j++)
{
if(j>=a[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i]]+a[i]);
else dp[i][j]=dp[i-1][j];
}
}
cout<<sum-dp[n][v];
return 0;
}
再利用一下滚动数组进行空间优化:
#include<iostream>
using namespace std;
const int N=35;
const int M=1000005;
int a[N];
int n,x,sum=0,dp[M];
int main() {
cin>>n>>x;
for(int i=1;i<=n;i++)
{
cin>>a[i];sum+=a[i];
}
int v=sum-x;
for(int i=1;i<=n;i++)
{
for(int j=v;j>=a[i];j--)
{
dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
}
}
cout<<sum-dp[v];
return 0;
}
题目三:防疫大数据【100分】
- 按照以前的csp认证惯例,第三题是大模拟题,静下心来,慢慢做,变量什么的直接用题目里的变量表示就行了,不用再自己编变量;
- 大致思路就是,先把所有的用户漫游记录和风险地区记录存起来,最后在每天遍历近七日的游客记录,再根据题目的三个要去进行筛选遍历即可;
- 注意一个问题:题目中的日期可以是负数,从最后30分能明显看出来,dij的范围为[-1e5,i],因此用普通的一维或者二维数组就会出错,此时用map是最好的,map支持[]获取数值,并且[]内允许索引为负数(几乎任何类型)
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef struct Node
{
int d;
int u;
int r;
}UserNode,*User;
map<int,vector<User>>manyou; //第i天的漫游记录
int n,ri,mi;
set<int>ans; //有序的输出用户列表
map<int,map<int,int>>fengxian; //每个风险地区对应的风险日期
int main() {
cin>>n;
for(int i=0;i<n;i++)
{
cin>>ri>>mi;
for(int j=0;j<ri;j++)
{
int t;cin>>t;
for(int k=i;k<i+7;k++) fengxian[t][k]=1;
}
for(int j=0;j<mi;j++)
{
User user=new UserNode;
cin>>user->d>>user->u>>user->r;
manyou[i].push_back(user);
}
for(int k=i;k>i-7;k--) //遍历近7日的漫游数据
{
int len=manyou[k].size();
for(int j=0;j<len;j++)
{
User user=manyou[k][j];
if((user->d>i-7&&user->d<=i)&&fengxian[user->r][user->d]) //到访的那一天处于风险状态
{
//该地区从到访日到目前持续处于风险状态
bool f=true;
for(int h=user->d;h<=i;h++)
{
if(!fengxian[user->r][h])
f=false;
}
if(f) ans.insert(user->u);
}
}
}
cout<<i<<" ";
for(set<int>::iterator it=ans.begin();it!=ans.end();it++)
{
cout<<*it<<" ";
}
cout<<endl;
ans.clear();
}
return 0;
}