【刷题】【记录】牛客题单
题单地址:
【237题】算法基础精选题单_ACM竞赛_ACM/CSP/ICPC/CCPC/比赛经验/题解/资讯_牛客竞赛OJ_牛客网 (nowcoder.com)
【算法进阶题单】动态规划、数据结构、图论、数学_ACM竞赛_ACM/CSP/ICPC/CCPC/比赛经验/题解/资讯_牛客竞赛OJ_牛客网 (nowcoder.com)
第一章 模拟、枚举和贪心
模拟
NC16644 [NOIP2007]字符串的展开
#include<iostream> #include<algorithm> using namespace std; //isalpha() 函数 //默认是大小写英文字母,范围可改 //--情况,wa10% bool isnumber(char ch) { if(ch>='0' && ch<='9') return true; else return false; } string get_char(char l,char r,int p1,int p2,int p3) { //减号两侧同为小写字母或同为数字,且按照ASCII码的顺序,减号右边的字符严格大于左边的字符。 if( ( isalpha(l) && isalpha(r) && l<r ) || ( isnumber(l) && isnumber(r) && l<r ) ) { string ans=""; for(char t=l+1;t<r;t++) { for(int j= 0;j<p2;j++) ans+=t; } int ans_len=ans.length(); if( p1==2 && isalpha(l) ) { for(int i=0;i<ans_len;i++) ans[i] = ans[i]-'a'+'A'; } if(p1==3) { for(int i=0;i<ans_len;i++) ans[i] = '*'; } if(p3==2) reverse(ans.begin(),ans.end()); return ans; } else return "-"; } int main() { int p1,p2,p3; cin >> p1 >> p2 >> p3; string s; cin >> s; string ans = ""; int s_len = s.length(); for(int i=0;i<s_len;i++) { //边界判断,wa10% if(s[i]=='-' && i+1<s_len && i-1>=0 ) ans += get_char(s[i-1],s[i+1],p1,p2,p3); else ans += s[i]; } cout << ans; return 0; }
枚举
思路:合适的枚举对象、顺序、方法
合适的枚举顺序:
NC16593 [NOIP2011]铺地毯【逆向思维】
//思路,单点查询 => 倒序寻找 #include <bits/stdc++.h> using namespace std; const int N=1e4+10; int memory[N][4]; int main() { int n; cin>>n; for(int i=1;i<=n;i++) { for(int j=0;j<4;j++) cin>>memory[i][j]; } int x,y; cin>>x>>y; for(int i=n;i>=1;i--) { //铺设地毯的左下角的坐标(a,b)以及地毯在x轴和y轴方向的长度。 if(memory[i][0] <= x && memory[i][1] <= y ) if(memory[i][0]+memory[i][2]-1 >= x && memory[i][1]+memory[i][3]-1 >= y ) { printf("%d\n",i); return 0; } } printf("-1\n"); return 0; }
第二章: 递归、分治
递归:
NC15173 The Biggest Water Problem
#include<bits/stdc++.h> using namespace std; long long get_sum(long long x) { long long t=0; while(x) { t+=x%10; x/=10; } return t; } int main() { long long a; long long sum=0; cin>>a; while(a>0) { sum = get_sum(a); if(sum < 10 ) { printf("%lld",sum); return 0; } a=sum; } return 0; }
分治:
NC16660 [NOIP2004]FBI树
// 我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。 // FBI树是一种二叉树[1],它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2N的“01”串S可以构造出一棵FBI树T,递归的构造方法如下: // 1) 根结点为R,其类型与串S的类型相同; // 2) 若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1和S2;由左子串S1构造R的左子树T1,由右子串S2构造R的右子树T2。 // 现在给定一个长度为2N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历[2]序列。 #include <bits/stdc++.h> using namespace std; const int N=1e5+10;//数组大小猜的 int rt; struct node { char v; int lc,rc; }tree[N]; int tot; string s; char find(int l,int r) { bool find_0=false,find_1=false; for(int i=l;i<=r;i++) { if(find_0 && find_1 ) break; if(s[i] == '0') find_0=true; if(s[i] == '1') find_1=true; } if(find_0 && find_1 ) return 'F'; else if(find_0 ) return 'B'; else return 'I'; } void build(int &root,int l,int r) { root=tot++; tree[root].lc = tree[root].rc = -1; tree[root].v = find(l,r); if(l == r) return ; else { int mid = (l+r)>>1; build(tree[root].lc,l,mid); build(tree[root].rc,mid+1,r); return ; } } void post_order(int root) { if(tree[root].lc !=-1) post_order(tree[root].lc); if(tree[root].rc !=-1) post_order(tree[root].rc); printf("%c",tree[root].v); } int main() { int n; cin>>n; cin>>s; build(rt,0,s.length()-1); post_order(rt); return 0; }
第三章: 二分、三分、01分数规划
二分:
二分查找:
NC235558 牛可乐和魔法封印
#include<bits/stdc++.h> using namespace std; int n,q; const int N=1e5+10; int d[N]; int main() { cin>>n; for(int i=0;i<n;i++) cin>>d[i]; sort(d,d+n); cin>>q; while(q--) { int a,b; cin>>a>>b; //似乎不能使用L,R作为变量名,会出错 int lpos= lower_bound(d,d+n,a)-d ; int rpos= upper_bound(d,d+n,b)-d ; printf("%d\n",rpos-lpos); } return 0; }
第四章: 栈、队列
栈:
NC14326 Rails
#include<bits/stdc++.h> using namespace std; int n,q; const int N=1e3+10; int d[N]; stack <int > sta; int main() { int n; bool first = true; while(scanf("%d",&n) && n!=0 ) { if(!first ) printf("\n"); while(scanf("%d",&d[0]) && d[0]!=0 ) { if(!first ) printf("\n"); else first=false; while(!sta.empty() ) sta.pop(); for(int i=1;i<n;i++) cin>>d[i]; int nw=0,pos=1; bool fail=false; while(nw<n && !fail ) { if(!sta.empty() && d[nw] == sta.top() ) sta.pop(),nw++; else if(d[nw] < pos ) fail=true; else if(d[nw] == pos ) nw++,pos++; else sta.push(pos++); } if(!fail ) printf("Yes"); else printf("No"); } } return 0; }
队列:
NC13822 Keep In Line
#include <bits/stdc++.h> using namespace std; int T,n; queue <int > q; int tot; map <string ,int > id; const int N=1e5+10; bool out[N]; int main() { int cnt=0;//不插队人数 cin>>T; while(T--) { while(!q.empty() ) q.pop(); tot = 0; id.clear(); cnt = 0; //这个不写wa一次... string s1,s2; cin>>n; for(int i=1;i<=n;i++) { cin>>s1>>s2; if(s1[0]=='i' ) { id[s2] = ++tot; q.push(tot); cnt++; //cout << s2 << " "<<tot<<endl; //cout<<cnt<<endl; } else { if(q.front() != id[s2] ) { cnt--; out[id[s2]]=true; } else { q.pop(); while(!q.empty() && out[q.front()] ) q.pop(); } //cout<<cnt<<endl; } } printf("%d\n",cnt); for(int i=1;i<=tot;i++) out[i]=false; } return 0; }
第五章:优先队列、并查集
堆:
模板:
NC16663 合并果子
#include<bits/stdc++.h> using namespace std; int main() { priority_queue <int , vector <int > ,greater <int > > q; int n; cin >> n; for(int i=1,x;i<=n;i++) cin>>x,q.push(x); long long sum = 0; while(!q.empty() ) { long long t = q.top(); q.pop(); if(q.empty() ) break; t += q.top(); q.pop(); q.push(t); sum += t ; } cout<<sum; return 0; }
NC214362 第k小
#include <bits/stdc++.h> using namespace std; int n,m,k; const int N=2e5+10; //greater 改成小根堆 priority_queue <int ,vector <int > ,greater <int > > q1; //放置 k+1 到 n 的数字 priority_queue <int ,vector <int > ,less <int > > q2; //放置 1 到 k 的数字 void get(int x) //限制条件为,q2一定满了,q1中可能有数 { int cnt_sub = 0 ; if(q2.top() > x) //实际上x只会挤走一个q2中的数 { q1.push(q2.top() ); q2.pop(); q2.push(x); } else q1.push(x); } int main() { cin>>n>>m>>k; int nw_n=0; int v; for(int i=1;i<=n;i++) { cin>>v; nw_n ++; if(nw_n <= k ) q2.push(v); else get(v); } int op; while(m--) { cin>>op; if(op==1 ) { cin>>v; nw_n++; if(nw_n <= k ) q2.push(v); else get(v); } else { if(nw_n < k ) printf("-1\n"); else printf("%d\n",q2.top() ); } } return 0; }
第六章:搜索
DFS:
回溯思想:
NC15128 老子的全排列呢
#include<bits/stdc++.h> using namespace std; bool us[10]; int d[10]; void dfs(int pos) { if(pos >= 8 ) { for(int i=0;i<8;i++) { if(i!=0) printf(" "); printf("%d",d[i]); } printf("\n"); return ; } else { for(int i=1;i<=8;i++) if(!us[i]) { d[pos] = i; us[i] = true; dfs(pos+1); us[i] = false; } } } int main() { dfs(0); return 0; }
BFS:
NC15136 迷宫
#include <bits/stdc++.h> using namespace std; int n,m; const int N=510; struct node { int x,y; bool key; int step; }; queue <node > q; int py[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; int mp[N][N]; int step[N][N][2]; //0表示能走,1表示钥匙,-1表示墙壁,D表示门锁 int main() { int sx,sy,ex,ey; cin>>n>>m; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { char ch = getchar(); while(ch!='W' && ch!='K' && ch!='S' && ch!='D' && ch!='E' && ch!='.') ch = getchar(); if(ch == 'W' ) mp[i][j]=-1; else if(ch == 'K' ) mp[i][j]=1; else if(ch == 'D' ) mp[i][j]=2; else if(ch == 'S') sx = i , sy = j; else if(ch == 'E' ) ex = i , ey = j; } } // for(int i=1;i<=n;i++) // { // for(int j=1;j<=m;j++) // { // printf("%3d ",mp[i][j]); // } // printf("\n"); // } //printf("%d %d \n",ex,ey); q.push({sx,sy,false,0}); while(!q.empty() ) { node t = q.front(); q.pop(); for(int i=0;i<4;i++) { int nx = t.x + py[i][0]; int ny = t.y + py[i][1]; if(nx<1 || ny<1 || nx>n || ny>m ) continue; if(mp[nx][ny] == -1 ) continue; if(mp[nx][ny] == 2 && !t.key ) continue; bool t_key = t.key; if(mp[nx][ny] == 1 ) t_key = true; if(step[nx][ny][t_key]>0 ) continue; step[nx][ny][t_key] = t.step +1; //printf("%d %d %d %d\n",nx,ny,t_key,t.step+1); if(nx == ex && ny == ey ) { printf("%d\n",t.step+1); return 0; } q.push({nx,ny,t_key,t.step+1}); } } printf("-1\n"); return 0; }
第七章:
递推:
NC235911 走楼梯
#include<bits/stdc++.h> using namespace std; const int N=110; int f[N]; int main() { int n; cin>>n; f[0]=1,f[1]=1; for(int i=2;i<=n;i++) f[i] = f[i-1]+f[i-2]; printf("%d\n",f[n]); return 0; }
线性dp:
NC22096 数字三角形(什么算法也没用,很水)
#include <bits/stdc++.h> using namespace std; int main() { int n; while(~scanf("%d",&n) ) { for(int i=1;i<=n;i++) { for(int j=1;j<=i;j++) { if(j!=1 ) printf(" "); printf("%d",j); } printf("\n"); } } return 0; }
背包问题:
01背包:
NC16693 装箱问题
#include <bits/stdc++.h> using namespace std; const int M=2e5+10; bool f[M]; int main() { int V,n; cin>>V>>n; f[0]=true; for(int i=1;i<=n;i++) { int v; cin>>v; for(int j=V;j>=v;j--) { if(f[j-v] ) f[j]=true; } } for(int j=V;j>=0;j--) if(f[j] ) { printf("%d",V-j); return 0; } return 0; }
第八章:
树形dp:
【基本概念-笔记】:【笔记】树形dp - 心若笺诗 - 博客园 (cnblogs.com)
【基本算法-笔记】:【笔记】【树形dp】 - 心若笺诗 - 博客园 (cnblogs.com)
NC15033 小G有一个大树
#include<bits/stdc++.h> using namespace std; const int N=1e3+10; int n; vector <int > son[N]; int point,num; int f[N],sum[N]; //sum是x点子树的所有节点数,不包括自己 //f是去掉x,x点子树中节点最多的连通块 void dfs(int x,int fr) { int sz=son[x].size(); sum[x] = 1; for(int i=0;i<sz;i++) { int nx=son[x][i]; if(nx==fr ) continue; dfs(nx,x); sum[x] += sum[nx]; } for(int i=0;i<sz;i++) { int nx=son[x][i]; if(nx==fr ) continue; f[x] = max(f[x],sum[nx]); } int t = max(f[x],n-sum[x]); if(t < num ) num=t,point=x; } int main() { cin>>n; for(int i=1;i<n;i++) { int u,v; cin>>u>>v; son[u].push_back(v); son[v].push_back(u); } num=n; dfs(1,0); printf("%d %d",point,num); return 0; }
第九章:
基本概念:
NC16679 [NOIP2003]神经网络
(本题简略题解:【图论】【刷题】【蓝绿题】【强连通】【拓扑】 - 心若笺诗 - 博客园 (cnblogs.com))
#include<cstdio> #include<cstdlib> #include<queue> #include<vector> using namespace std; int n,m; const int N=103; int c[N],in[N],sz[N]; struct node { int v,w; node(int vv,int ww) { v=vv,w=ww; } node(){} }; vector <node > g[N]; queue <int > q; void topo_sort() { while(!q.empty()) { int t=q.front();q.pop(); sz[t]=g[t].size() ; for(int i=0;i<sz[t];i++) { node v=g[t][i]; if(c[t]>0) c[v.v ]+=c[t]*v.w ; if(--in[v.v ]==0) q.push(v.v ); } } } int main() { scanf("%d%d",&n,&m); int x; for(int i=1;i<=n;i++) { scanf("%d%d",&c[i],&x); if(c[i]>0) q.push(i); else c[i]-=x; } int u,v,w; while(m--) { scanf("%d%d%d",&u,&v,&w); g[u].push_back(node(v,w)); in[v]++; } topo_sort(); bool fail=true; for(int i=1;i<=n;i++) if(!sz[i] && c[i]>0) { printf("%d %d\n",i,c[i]); fail=false; } if(fail) printf("NULL\n"); return 0; }
NC20115 [HNOI2015]菜肴制作
(感觉还蛮有写头的,所以另开了篇题解)
第十章:
数论:
NC14399 素数判断
#include<bits/stdc++.h> using namespace std; int T; vector <int > v; const int N=1e5; bool notprime[N+10]; void prepare() { for(int i=2;i<=N;i++) if(!notprime[i] ) { v.push_back(i); for(int j=2;i*j<=N;j++) notprime[i*j]=true; } } int sz; bool check (int x) { for(int i=0;i<sz && v[i]<x ;i++) if(x%v[i] == 0 ) return false; return true; } int main() { prepare(); //1e5范围内的素数都取出来 sz = v.size(); cin>>T; while(T--) { int n; cin>>n; if(check(n) ) { printf("isprime\n"); printf("%d\n",n); } else { printf("noprime\n"); bool first = true; for(int i=0; i<sz && v[i]<=n ;i++) { if(n%v[i] == 0 ) { if(first ) first = false; else printf(" "); printf("%d",v[i]); while(n % v[i] == 0 ) n/=v[i]; } } //因为下面这句话我wa了4个点... if(n != 1 ) printf(" %d",n); //比如p>1e5且p是素数,原来算法对于2p,不会输出p printf("\n"); } } return 0; }
第十一章:
线段树:
NC15164 Big Water Problem
【代码1】树状数组模板
#include <bits/stdc++.h> using namespace std; int n; const int N = 1e5 + 10; int a[N], c[N]; // a[i]为原始数组 // c[i]为树状数组 int lowbit(int x) { return x & -x; } void build() //利用a[i]数组数据建立c[i] { for (int i = 1; i <= n; i++) { c[i] = a[i]; int k = i - lowbit(i) + 1; for (int j = k; j < i; j++) c[i] += a[j]; } } int sum(int x) { int sum = 0; while (x > 0) { sum += c[x]; x -= lowbit(x); } return sum; } int query_sum(int l, int r) { return sum(r) - sum(l - 1); } void add(int pos, int x) { while (pos <= n) { c[pos] += x; pos += lowbit(pos); } } int main() { int m; cin >> n >> m; for (int i = 1; i <= n; i++) cin >> a[i]; build(); int op, a, b; while (m--) { cin >> op >> a >> b; if (op == 1) add(a, b); else cout << query_sum(a, b)<<endl; } return 0; }
【代码2】线段树模板
#include <bits/stdc++.h> using namespace std; int n; const int N = 1e5 + 10; int d[N]; int rt, tot; struct node { int l, r; //区间[l,r] int lc, rc; //左右子节点 int v; //区间整体的值 } tree[N * 3]; void push_up(int x) { tree[x].v = 0; if (tree[x].lc != -1) tree[x].v += tree[tree[x].lc].v; if (tree[x].rc != -1) tree[x].v += tree[tree[x].rc].v; return; } void create(int &root, int l, int r) { if (l > r) return; root = tot++; tree[root].l = l, tree[root].r = r; if (l == r) { tree[root].lc = tree[root].rc = -1; tree[root].v = d[l]; } else { int mid = (l + r) >> 1; create(tree[root].lc, l, mid); create(tree[root].rc, mid + 1, r); push_up(root); } } void change(int root, int pos, int v) //单点修改 { if (tree[root].l == tree[root].r) tree[root].v = v; else { int mid = (tree[root].l + tree[root].r) >> 1; if (pos <= mid) change(tree[root].lc, pos, v); else change(tree[root].rc, pos, v); push_up(root); } } int query(int root, int l, int r) //当前查找的节点为root,l,r为需要查找的区间看两者是否符合 { if (l <= tree[root].l && tree[root].r <= r) return tree[root].v; else { int mid = (tree[root].l + tree[root].r) >> 1, sum = 0; if (l <= mid) sum += query(tree[root].lc, l, r); if (r > mid) sum += query(tree[root].rc, l, r); return sum; } } int main() { int m; cin >> n >> m; for (int i = 1; i <= n; i++) cin >> d[i]; create(rt, 1, n); while (m--) { int op, a, b; cin >> op >> a >> b; if (op == 1) { d[a]+=b; change(rt, a, d[a]); } else cout << query(rt, a, b) << endl; } return 0; }
第十二章:
LCA模板题
DFS序:
NC13611 树(dfs序+区间dp)
做不倒,现学数论在补了已经呜呜