L1-01 人与神 (5 分)
分析:
无脑题,直接干
#include
using namespace std;
int main(){
cout<<"To iterate is human, to recurse divine.";
return 0;
}
L1-02 两小时学完C语言 (5 分)
分析:
简单减法题。
#include
using namespace std;
int main(){
int n,k,m;
cin>>n>>k>>m;
cout<<n-k*m;
return 0;
}
L1-03强迫症 (10 分)
分析:
输入的时候用字符串输入,然后分别分析字符串长度len为6和4的情况。再记录一下len为4的时候,前两个字符串的整数值,若小于22则输出20,否则输出19。其他字符只需要按照模板直接输出即可。
#include
#include
using namespace std;
int main(){
char str[10];
cin>>str;
int len=strlen(str);
if(len==6) {
for(int i=0;i<4;i++) cout<<str[i];
cout<<"-";
for(int i=4;i<6;i++) cout<<str[i];
}
else{
int n=(str[0]-'0')*10+(str[1]-'0');
if(n<22) cout<<"20";
else cout<<19;
cout<<str[0]<<str[1]<<"-"<<str[2]<<str[3];
}
return 0;
}
L1-04 降价提醒机器人 (10 分)
分析:
注意一下输出保留一位小数的格式即可。
#include
using namespace std;
int main(){
int n,m;
double c;
cin>>n>>m;
for(int i=1;i<n;i++){
cin>>c;
if(c<m) printf("On Sale! %.1lf\n",c);
}
cin>>c;
if(c<m) printf("On Sale! %.1lf",c);
return 0;
}
L1-05大笨钟的心情 (15 分)
分析:
注意一下结尾无空行,用一个数组a[30]存储0-23小时的心情,然后每一小时t的心情便是a[t]。
#include
using namespace std;
int main(){
int a[30],x;
for(int i=0;i<=23;i++) cin>>a[i];
cin>>x;
if(x<0||x>23) return 0;
else {
cout<<a[x]<<" ";
if(a[x]>50) cout<<"Yes";
else cout<<"No";
};
while(cin>>x){
if(x<0||x>23) break;
else{
cout<<"\n"<<a[x]<<" ";
if(a[x]>50) cout<<"Yes";
else cout<<"No";
}
}
return 0;
}
L1-06 吉老师的回归 (15 分)
分析:
难点在于输入。需要输入可以输入一整行字符串包括有空格,所以可用gets()和getline()函数,当然记得用getchar()吃回车,不然输入会出错。
#include
using namespace std;
int main(){
int n,m;cin>>n>>m;
string str;
queue<string>q;
getchar();
for(int i=1;i<=n;i++){
getline(cin,str);
if(str.find("easy")!=-1||str.find("qiandao")!=-1) continue;
else q.push(str);
}
while(!q.empty()&&m--){
q.pop();
}
if(q.empty()) cout<<"Wo AK le";
else cout<<q.front();
return 0;
}
L1-07天梯赛的善良 (20 分)
分析:
用函数sort给序列a[1…n]排个序,那么第一个a[1]则是最小的,最后一个a[n]则是最大的。然后遍历一遍,分别记录值为a[1]和a[n]的个数。
#include
using namespace std;
const int maxn=2e4+10;
int a[maxn];
int main(){
int n,num1=0,num2=0;cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
for(int i=1;i<=n;i++){
if(a[i]==a[1]) num1++;
if(a[i]==a[n]) num2++;
}
cout<<a[1]<<" "<<num1<<"\n"<<a[n]<<" "<<num2;
return 0;
}
L1-08 乘法口诀数列 (20 分))
分析:
因为每一个元素的范围是0-9,所以两个数的乘积最大为81,所以对于每一次乘积分别考虑它为个位数和十位数即可。再从i=2开始遍历,用x记录乘积值a[i]*a[i-1]。
#include
using namespace std;
int main(){
int a[1005],n,k=2,i=2;
cin>>a[1]>>a[2]>>n;
while(k<=n){
int x=a[i]*a[i-1];
if(x>=10) a[++k]=x/10,a[++k]=x%10;
else a[++k]=x;
i++;
}
for(int i=1;i<n;i++) cout<<a[i]<<" ";
cout<<a[n];
return 0;
}
L2-01 包装机 (25 分)
分析:
考察栈和队列的应用。通过题意可知,框服从后进先出的原则,而轨道和流水线都服从先进先出的原则;所以可用栈来维护框中的元素,用队列来维护轨道和流水线中的元素。再不断输入一个整数x,分别讨论x==-1;x==0以及x为其他值的情况即可。
#include
using namespace std;
int main(){
int N,M,S,x;
cin>>N>>M>>S;
queue<char>q,qs[105];
stack<char>s;
for(int i=1;i<=N;i++){
for(int j=1;j<=M;j++){
char str;cin>>str;
qs[i].push(str);
}
}
while(cin>>x){
if(x==-1) break;
else if((x==0&&!s.empty())){
q.push(s.top()); s.pop();
}
else {
if(qs[x].empty()) continue;
if((s.size()==S&&!s.empty())){
q.push(s.top());
s.pop();
}
s.push(qs[x].front()); qs[x].pop();
}
}
while(!q.empty()) {
cout<<q.front();
q.pop();
}
return 0;
}
L2-02 病毒溯源 (25 分)
分析:
显然这是一道深搜题(dfs)。只需要先找到病毒的源头,再对它进行深搜便可以很快得到结果。那么如何找到病毒的源头呢?由于题目提示,所有的病毒都是靠变异而来,那么从0…n-1序列中唯一一个没有在编译序列中出现的元素便是病毒源头。由于题目还说明了要输出序列元素最小的最长链,那么对于每一个元素的变异序列我们可以用邻接表来维护,并每一个元素的邻接表通过函数sort()升序,这样在dfs过程中便可以保证第一次搜到的最长串便是元素最小的。
#include
using namespace std;
const int maxn=1e4+10;
int a[maxn],maxx=0,visit[maxn];
vector<int>g[maxn],ans;
void Init(){
memset(visit,0,sizeof(visit));
for(int i=1;i<=maxn;i++) g[i].clear();
}
void dfs(int u,int step){
a[step]=u;
if(g[u].size()==0){
if(step>maxx) {
maxx=step; ans.clear();
for(int i=1;i<=step;i++) ans.push_back(a[i]);
}
}
else{
for(int i=0;i<g[u].size();i++){
int v=g[u][i]; dfs(v,step+1);
}
}
}
int main(){
int n,pos; cin>>n;
Init();
for(int i=0;i<n;i++){
int opt;cin>>opt;
while(opt--){
int x;cin>>x;
g[i].push_back(x);
visit[x]=1;
}
sort(g[i].begin(),g[i].end());
}
for(int i=0;i<n;i++) if(!visit[i]) {
pos=i; break;
}
dfs(pos,1);
cout<<maxx<<"\n"<<*(ans.begin());;
for(vector<int>::iterator it=ans.begin()+1;it!=ans.end();it++) cout<<" "<<*it;
return 0;
}
L2-03 清点代码库 (25 分)
分析:
非常考察STL容器的熟悉应用。本题我用到了三个容器,vector,pair和map。首先通过输入我们可以发现,我们可以用vector来维护每一个模块,然后我们需要维护一个map容器将vector映射到该模块出现的次数上。显然,map容器的大小就是模块的个数。那么如何按照要求让模块出现次数从大到小,模块出现次数相同的模块从小到大输出呢?我们不妨来分析一下孰轻孰重啊。很显然,一个元素的排序是比一个模块的排序简单得多,所以我们可以考虑,在维护容器map的时候就先维护一下模块的排序。那么如何维护呢?STL中默认容器mapmp的内部结构是按照key从小到大排序。并且mp.first的值就是key的值,mp.second的值就是value的值。 所以我们可以创建容器map为map;这样我们就已经维护好了模块是升序的。那接下我们如何再维护value是降序的呢?我们需要将map中的元素输出到vector中,通过pair进行排序。所以维护一个vector容器,内部的元素为pair对,该对的第一个元素为模块出现次数,第二个元素为模块;即vector > >ans ;为什么要让第一个元素是模块出现次数呢?因为STL中规定对vector> >ans进行排序时,默认是对pair对的first元素进行排序。 所以我们可以借助sort()函数再对该vector容器进行排序。但是最后又如何让容器中的第一个元素(模块出现次数)降序排序,而第二个元素(模块)继续保持相对升序呢?我们可以在map中的元素输出到vector的时候,将模块次数的相反数输出。最后sort(ans.begin(),ans.end())给vector升序。这样就可以保持模块的相对升序次序不变,并且对模块出现次数的相反数升序排序。最后输出的时候再输出模块相反数的相反数即可。
#include
using namespace std;
const int maxn=2e5+10;
map<vector<int>,int>mp;
vector<pair<int,vector<int> > >v;
inline int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
int main(){
int n,m,k=0;
n=read();m=read();
for(int i=1;i<=n;i++){
vector<int>temp;
int x;
for(int j=1;j<=m;j++){
x=read(); temp.push_back(x);
}
mp[temp]++;
}
printf("%d\n",mp.size());
for(auto it:mp) v.push_back({-it.second,it.first});
sort(v.begin(),v.end());
for(auto it:v){
printf("%d",-it.first);
vector<int>ans=it.second;
for(auto i:ans) printf(" %d",i);
printf("\n");
}
return 0;
}
`
L2-04 哲哲打游戏 (25 分)
分析:
题目很简单,难点在于读懂题。可用vector维护每一个剧情点每一个操作的结果,也可用二维数组维护;再额外开一个数组维护存档的值,分别讨论操作类型为0、1和2的情况即可。
#include
using namespace std;
const int maxn=1e5+10;
int book[maxn],ans=1;
vector<int>g[maxn];
int main(){
int n,m; cin>>n>>m;
for(int i=1;i<=n;i++){
int opt; cin>>opt;
while(opt--){
int x;cin>>x;
g[i].push_back(x);
}
}
for(int i=1;i<=m;i++){
int opt,x;cin>>opt>>x;
if(opt==0) ans=g[ans][x-1];
else if(opt==1) {
book[x]=ans;
cout<<ans<<endl;
}
else ans=book[x];
}
cout<<ans;
return 0;
}
L3-01 森森旅游 (30 分)
分析:
看完题,相信大多数人就已经能想到本题大致的算法是Dijkstra算法。我们可以对每一个城市i来分析,很显然如果在城市i兑换了旅游金,那么1…i之间用的都是现金c, 城市i…n之间用的都是旅游金d。所以我们需要维护两个单源最短路,并且是两个相反的单源最短路;即一条是从起点1开始到任意节点的单源最短路,权值为现金c;我们记录该路为d1[maxn]。另一条是从起点n开始到任意节点的单源最短路,权值为旅游金d,我们记录为d2[maxn]。这样我们便可以求出在任意一个结点i兑换了旅游金以后花费的最少现金ans[i],即d1[i]+(d2[i]+d[i]-1)/a[i];。我们可以用一个mutliset容器来维护每一个ans[i];为啥用mutliset呢?因为它默认升序并且可以有重复元素。然后在更改汇率的时候,我们只需要先找到需要更改汇率的城市x,将它之前的花费ans[x]从容器中删除再添加汇率改变后新的花费ans[x]'即可;最后输出容器中第一个元素便是答案。
时间复杂度:O(2*mlogn+qlogn)
蒻蒻只得了21分,玄学错误看不明白;希望看到错误的大佬可以提醒我。
#include
using namespace std ;
const int maxn=1e5+10;
typedef long long ll;
const ll INF=1e18;
multiset<ll>ms;
struct Edge{
ll v,w;
bool operator <(const Edge&b) const{
return w>b.w;
};
};
vector<Edge>gc[maxn],gd[maxn];
ll n,m,q,a[maxn],d1[maxn],d2[maxn],visit[maxn],ans[maxn];
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
void Init(){
memset(visit,0,sizeof(visit));
}
void Dijkstar(vector<Edge>g[],ll d[],ll s){
for(int i=1;i<=n;i++) d[i]=INF;
priority_queue<Edge>q;
q.push({s,0});
d[s]=0;
while(!q.empty()){
Edge e=q.top();q.pop();
ll u=e.v;
if(visit[u]) continue;
visit[u]=1;
for(int i=0;i<g[u].size();i++){
ll v=g[u][i].v,w=g[u][i].w;
if(w+d[u]<d[v]) {
d[v]=w+d[u];
q.push({v,w});
}
}
}
}
int main(){
memset(ans,0,sizeof(ans));
n=read(),m=read(),q=read();
for(int i=1;i<=m;i++){
int u=read(),v=read(),c=read(),d=read();
gc[u].push_back({v,c});
gd[v].push_back({u,d});
}
Init(); Dijkstar(gc,d1,1);
Init(); Dijkstar(gd,d2,n);
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++){
if(d1[i]==INF||d2[i]==INF) continue;
ans[i]=d1[i]+(d2[i]+a[i]-1)/a[i];
ms.insert(ans[i]);
}
for(int i=1;i<=q;i++){
ll x=read(),y=read();
if(d1[x]!=INF&&d2[x]!=INF) {
ll res=d1[x]+(d2[x]+a[x]-1)/a[x]; ms.erase(ms.find(res));
a[x]=y; ms.insert(d1[x]+(d2[x]+a[x]-1)/a[x]);
}
multiset<ll>::iterator it=ms.begin();
if(i!=q) cout<<*it<<endl;
else cout<<*it;
}
return 0;
}
L3-02 还原文件 (30 分)
分析:
蒻蒻太菜了,看完大佬的思想才知道这道题可以使用dfs写。用dfs的思路就很简单了;我们可以搜索每一个折线角(实际上只会搜索到每一个碎片的起始折线角)。然后直接暴力比较每一个碎片,看看该碎片的每一个折线角是否满足要求;若是满足要求则记录该碎片的序号并标记为已访问。;按照这样可以得26分,那另外4分又是怎么回事呢? 我们可以考虑这样一种情况,如果一个碎片是另一个碎片的前缀,那么结果是不是可能紊乱呢?答案是肯定的;所以我们还需要回溯再搜索,也就是加上我在代码中注释的那一行即可。
时间复杂度:大概是O(m*n)。
#include
using namespace std;
const int maxn=1e5+10;
int h[maxn],n,m;
bool vis[maxn],opt=false;
vector<int>v[maxn],ans;
void dfs(int pos){ //当前在第pos个折线角位置
if(opt) return ;
if(pos>=n){
opt=true;
cout<<*(ans.begin());
for(vector<int>::iterator it=ans.begin()+1;it!=ans.end();it++) cout<<" "<<*it;
return ;
}
for(int i=1;i<=m;i++){
if(vis[i]) continue;
bool flag=true;
for(int j=0;j<v[i].size();j++){
if(h[pos+j]!=v[i][j]) {flag=false;break;}
}
if(!flag) continue;
ans.push_back(i);vis[i]=true;
dfs(pos+v[i].size()-1);
ans.pop_back();vis[i]=false; //不加这一行回溯的话只能拿26分
}
}
int main(){
iostream::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++) cin>>h[i];
cin>>m;
for(int i=1;i<=m;i++) vis[i]=false;
for(int i=1;i<=m;i++){
int k,x;cin>>k;
while(k--) {cin>>x;v[i].push_back(x);}
}
dfs(1);
return 0;
}