csp-s模拟赛191104
problem A:
首先看到题,肯定想到的就是一个类似完全背包的问题。
用f[i]表示i能否用a[i]中的数组成。
数据已经保证数组a有序,于是我们考虑,我们考虑每一个数能否被它前面的数组成。
若x能被前i个数组成,且组成x的数当中包含a[i],则x-a[i]也能被前i个数组成。
那么容易得到转移方程f[j] |= f[j-a[i]]
于是我们就可以这样做:
#include<bits/stdc++.h> using namespace std; inline int read(){ int data=0,w=1;char ch=0; while(ch!='-' && (ch<'0'||ch>'9'))ch=getchar(); if(ch=='-')w=-1,ch=getchar(); while(ch>='0' && ch<='9')data=data*10+ch-'0',ch=getchar(); return data*w; } const int maxn=10010; int a[maxn],f[maxn]; int n,q; int main(){ n=read(); for(int i=1;i<=n;i++)a[i]=read(); f[0]=1; for(int i=1;i<=n;i++) for(int j=a[i];j<=maxn;j++) f[j]=f[j]|f[j-a[i]]; q=read(); int x; while(q--){ x=read(); if(f[x])puts("Acesrc!"); else puts("Acesrc?"); } return 0; }
这样我们就拿到了50pts的部分分,因为再想开大也是拿不到分的了,256MB稳定可以开的数组是5e7。
于是我们考虑其它做法,我们考虑最小合法表示。
如果一个数>=最小合法表示,那么它就可以被a数组中的元素表示成,否则就不行。
a数组是有序的,最小的是a[1],我们就把一个数表示成k*a[1]+i。
任何数都可以表示成k*a[1]+i,如果能拼成k*a[1]+i,也就可以拼成(k+1)*a[1]+i。
于是我们用f[i]表示最小的合法的能够表示成k*a[1]+i的数是什么。
把每个f[i]视为一个点,问题就转化成了求f[0]到所有点的最短路。
代码:
#include<bits/stdc++.h> #define getchar gc using namespace std; char buf[1000010],*pos,*End; inline char gc(){ if(pos==End){ End=(pos=buf)+fread(buf,1,1000000,stdin); if(pos==End)return EOF; }return *pos++; } inline int read(){ int data=0,w=1;char ch=0; while(ch!='-' && (ch<'0'||ch>'9'))ch=getchar(); if(ch=='-')w=-1,ch=getchar(); while(ch>='0' && ch<='9')data=data*10+ch-'0',ch=getchar(); return data*w; } const int N=1e4+5; int n,q,a[N],dis[N],vis[N]; priority_queue<pair<int,int> > heap; void dijkstra(int s){ memset(dis,0x3f,sizeof dis); memset(vis,0,sizeof vis); dis[s]=0; heap.push(make_pair(-dis[s],s)); while(heap.size()){ int x=heap.top().second;heap.pop(); vis[x]=1; for(int i=1;i<=n;i++){ int y=(x+a[i])%a[1];//下一个数字 if(!vis[y] && dis[y]>dis[x]+a[i]){ dis[y]=dis[x]+a[i]; heap.push(make_pair(-dis[y],y)); } } } } int main(){ n=read(); for(int i=1;i<=n;i++) a[i]=read(); dijkstra(0); q=read(); for(int i=1;i<=q;i++){ int x=read(); if(x>=dis[x%a[1]])puts("Acesrc!"); else puts("Acesrc?"); } return 0; }
problem B: