近期学了一些新算法,并查集,最短路,线段树,树状数组,最小生成树,KMP,简单DP。
暂时就对以上这些算法进行总结与复习。
一、并查集
并查集是最简单的一种算法。
主要用于合并,查找。
基本代码
int par[N]; //父亲 int Rank[N]; //树的高度 int vis[N]; bool flag; void init(int n) { for(int i=0; i<=n; ++i) { par[i] = i; vis[i] = 0; } } int find(int a) { int r = a; while(par[r] != r) r = par[r]; int i = a; int j; while(i != r) { j = par[i]; par[i] = r; i = j; } return r; } void merge(int a,int b) { int A,B; A = find(a); B = find(b); if(A != B) { par[B] = A; } else { flag = 0; } }
int par[N]; //父亲 int Rank[N]; //树的高度 //初始化n个元素 void init(int n) { for(int i=0; i<=n; ++i) { par[i] = i; Rank[i] = 0; } } //查询树的根非递归实现 int find(int x) { while(par[x]!=x) x=par[x]; return x; } //合并x和y所属集合 void unite(int x,int y) { int fx=find(x); int fy=find(y); if(fx==fy) return; if(Rank[fx]>Rank[fy]) par[fx]=fy; else { par[fy]=fx; if(Rank[fx]==Rank[fy]) Rank[x]++; } } //关于路径压缩 int find2(int x) { int fx=find(x); int t; while(x!=fx) { t=par[x]; par[x]=fx; x=t; } return fx; }
How Many Tables
/** /*@author Victor /*language C++ */ //#include <bits/stdc++.h> #include<iostream> #include<algorithm> #include<cstdlib> #include<cstring> #include<cstdio> #include<string> #include<vector> #include<bitset> #include<queue> #include<deque> #include<stack> #include<cmath> #include<list> #include<map> #include<set> //#define DEBUG #define RI register int using namespace std; typedef long long ll; //typedef __int128 lll; const int N=100000+10; const int MOD=1e9+7; const double PI = acos(-1.0); const double EXP = 1E-8; const int INF = 0x3f3f3f3f; #define iput(n) scanf("%d",&n); #define dput(n) scanf("%lf",&n); #define llput(n) scanf("%lld",&n); #define puti(n) printf("%d ",n); #define putll(n) printf("%lld ",n); #define putd(n) printf("%lfd ",n); int par[N]; //父亲 int Rank[N]; //树的高度 //初始化n个元素 void init(int n) { for(int i=0; i<=n; ++i) { par[i] = i; Rank[i] = 0; } } //查询树的根非递归实现 int find(int x) { while(par[x]!=x) x=par[x]; return x; } //合并x和y所属集合 void unite(int x,int y) { int fx=find(x); int fy=find(y); if(fx==fy) return; if(Rank[fx]>Rank[fy]) par[fx]=fy; else { par[fy]=fx; if(Rank[fx]==Rank[fy]) Rank[x]++; } } //关于路径压缩 int find2(int x) { int fx=find(x); int t; while(x!=fx) { t=par[x]; par[x]=fx; x=t; } return fx; } int main() { #ifdef DEBUG freopen("input.in", "r", stdin); //freopen("output.out", "w", stdout); #endif int T; iput(T) //scanf("%d",&T); while(T--) { int n, m; iput(n) iput(m) //scanf("%d%d",&n,&m); init(n); for(int i = 0; i < m; i++) { int a,b; // iput(a) iput(b) // scanf("%d%d",&a,&b); unite(a,b); } int sum = 0; for(int i = 1; i <= n; i++) { if(find(i)==i) sum++; } puti(sum) //printf("%d ",sum); } }
最短路Dijkstra
const int maxn = 1200; int mp[maxn][maxn],dis[maxn],vis[maxn]; int k[maxn]; int n,m; void dijkstra(int x){ //初始化 for(int i = 0; i < n;i++){ dis[i] = mp[x][i]; vis[i] = 0; }
dis[x] = 0; vis[x] = 1; int j,k,temp; for(int i = 0;i < n;i++){ temp = INF; //又是循环写错 for(int j = 0;j < n;j++){ if(!vis[j]&&(temp > dis[j])){ k = j; temp = dis[j]; } } if(temp==INF) break; vis[k] = 1; for(int i = 0;i < n;i++){ if(!vis[i]&&(dis[i]>(dis[k]+mp[k][i]))) dis[i] = dis[k] + mp[k][i]; } } } int main(){ while(~scanf("%d%d",&n,&m)){ for(int i = 0;i < n;i++){ for(int j = 0;j < n ;j++){ mp[i][j] = INF; } } for(int i = 0;i < m;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); if(mp[u][v] > w){ mp[u][v] = mp[v][u] = w; } } int s,t; scanf("%d%d",&s,&t); dijkstra(s); if(dis[t]==INF) printf("-1 "); else printf("%d ",dis[t]); } }
dj是来记录从s出发到每一点的最短路
spfa判负环以及建图
int n , m ,tot,k; int head[N*10]; struct node{ int from; int to; int w; int next; }edge[N*10]; int dis[N*10],cnt[N*10],path[N*10]; bool inq[N*10]; void init(){ memset(head,-1,sizeof(head)); tot = 0; } void add(int from,int to,int w){ edge[tot].from = from; edge[tot].to = to; edge[tot].w = w; edge[tot].next = head[from]; head[from] = tot++; } //判负权边 bool spfa(int st){ //双向 deque<int>q; __cls(dis); _cls(inq); _cls(cnt); dis[st] = 0; q.push_back(st); inq[st] = true; cnt[st]++; while(!q.empty()){ int now = q.front(); q.pop_front(); inq[now] = 0; if(cnt[now] >= n){ return false; } for(int i = head[now];i!=-1;i = edge[i].next){ int nex = edge[i].to; if(dis[nex] > dis[now] + edge[i].w){ dis[nex] = dis[now] + edge[i].w; if(!inq[nex]){ inq[nex] = 1; cnt[nex]++; if(!q.empty()&&dis[nex] < dis[q.front()]){ q.push_front(nex); } else{ q.push_back(nex); } } } } } return true; } int main(){ int t,u,v,w; iput(t) for(int i = 1; i <= t; i++){ iput(n) iput(m) iput(k) init(); while(m--){ iput(u) iput(v) iput(w) add(u,v,w); add(v,u,w); } while(k--){ iput(u) iput(v) iput(w) add(u,v,-w); } if(spfa(1)){ printf("NO "); } else{ printf("YES "); } } return 0; }
最短路 队列 建图
struct Edge { int to; int dis; Edge(int to,int dis) { this -> to = to; this -> dis = dis; } }; vector<Edge>mp[maxn]; typedef pair<int,int>P; void init() { start = -1; cnt=0; for(int i = 0; i < maxn; i++) mp[i].clear(); ___cls(shead); ___cls(ehead); fill(sdis + 1,sdis + 1 + n,INF); fill(edis + 1,edis + 1 + n,INF); } void dijkstra(int dis[],int head[],int s) { priority_queue<P,vector<P>,greater<P> >q; while(!q.empty()) q.pop(); dis[s] = 0; q.push(P(0,s)); while(!q.empty()) { P p = q.top(); q.pop(); int v = p.second; if(p.first > dis[v]) continue; for(int i = 0; i < mp[v].size(); i++) { Edge& e = mp[v][i]; if(dis[v] + e.dis < dis[e.to]) { head[e.to] = v; dis[e.to] = dis[v] + e.dis; q.push(P(dis[e.to],e.to)); } } } } void doit() { dijkstra(sdis,shead,S); dijkstra(edis,ehead,E); ans = sdis[E]; } int main() { while(scanf("%d%d%d",&n,&S,&E)!=EOF) { init(); scanf("%d",&M); for(int i = 0; i< M; i++) { scanf("%d%d%d",&x,&y,&z); mp[x].push_back(Edge(y,z)); mp[y].push_back(Edge(x,z)); } doit(); scanf("%d",&k); for(int i = 0; i < k; i++) { scanf("%d%d%d",&x,&y,&z); if(sdis[x] + edis[y] + z < ans) { start = x; End = y; ans = sdis[x] + edis[y] + z ; } if(sdis[y] + edis[x] + z < ans) { start = y; End = x; ans = sdis[y] + edis[x] + z; } } if(cas) printf(" "); cas++; if(start == -1) { for(int i = E; ~i; i=shead[i]) path[cnt++] = i; for(int i = cnt-1; i >= 0; i--) { if(i == 0) printf("%d ",path[i]); else printf("%d ",path[i]); } printf("Ticket Not Used %d ",ans); } else { for(int i = start; ~i; i = shead[i]) path[cnt++] = i; reverse(path,path+cnt); for(int i = End; ~i; i = ehead[i]) path[cnt++] = i; for(int i = 0; i < cnt; i++) { if(i == cnt-1) printf("%d ",path[i]); else printf("%d ",path[i]); } printf("%d %d ",start,ans); } } return 0; }
using namespace std; const ll maxn=200000+10; const ll INF=1LL<<60; ll SUM[maxn]; ll n,m,t; ll head[maxn],cnt; struct edge { ll a; ll to,b,next; }mp[maxn]; void add(ll from,ll to,ll a,ll b) { mp[++cnt].next = head[from]; mp[cnt].to = to; mp[cnt].a = a; mp[cnt].b = b; head[from] = cnt; } struct EDGE { ll dis; ll to; EDGE(ll a,ll b) { to = a; dis = b; } bool operator <(const EDGE &k)const { return dis > k.dis; } }; void init() { SUM[0]=1; for(ll i = 1;i <= 100;i++) SUM[i] = SUM[i-1]<<1; } ll dis[maxn]; void dijkstra(ll s) { bool vis[maxn]; for(ll i = 0;i <= n;i++) dis[i] = INF; for(ll i = 0;i <= n;i++) vis[i] = false; dis[s] = 1; priority_queue<EDGE>q; q.push(EDGE(s,dis[s])); while(!q.empty()) { EDGE top = q.top(); q.pop(); if(vis[top.to]) continue; vis[top.to] = true; for(ll i = head[top.to];i;i=mp[i].next) { edge tmp = mp[i]; if(tmp.a / dis[top.to] +1 < SUM[tmp.b])continue; if(dis[tmp.to] > tmp.a + dis[top.to]) { dis[tmp.to] = tmp.a + dis[top.to]; q.push(EDGE(tmp.to,dis[tmp.to])); } } } }
最小生成树
struct edge { int u,v,cost; }eg[100001]; int n,m;//,father[100001]; bool cmp(edge e1,edge e2) { return e1.cost<e2.cost; } int par[N]; //父亲 int Rank[N]; //树的高度 //初始化n个元素 void init(int n) { for(int i=0; i<=n; ++i) { par[i] = i; Rank[i] = 0; } } //查询树的根非递归实现 int find(int x) { while(par[x]!=x) x=par[x]; return x; } //合并x和y所属集合 void unite(int x,int y) { int fx=find(x); int fy=find(y); if(fx==fy) return; if(Rank[fx]>Rank[fy]) par[fx]=fy; else { par[fy]=fx; if(Rank[fx]==Rank[fy]) Rank[x]++; } } //关于路径压缩 int find2(int x) { int fx=find(x); int t; while(x!=fx) { t=par[x]; par[x]=fx; x=t; } return fx; } // 最小生成树 Kruskal 算法 int Kruskal( ) { edge e; int i,res; sort(eg,eg+n,cmp); // 并查集 初始化 init(m); // 构建最小生成树 res=0; for( i=0;i<n;++i ) { e=eg[i]; if( find(e.u)!=find(e.v) ) { unite(e.u,e.v); res+=e.cost; } } return res; }
int main(){
// int n,m;
while(scanf("%d%d",&n,&m)&&n){
for(int i = 0; i < n;++i){
scanf("%d%d%d",&eg[i].u,&eg[i].v,&eg[i].cost);
}
int ans = Kruskal();
bool flag = 1;
for(int i = 2 ;i <= m;i++){
if(find(1)!=find(i)){
flag = 0;
break;
}
}
if(flag) printf("%d
",ans);
else printf("?
");
}
}
次小生成树
const int N = 1000 + 7 ; int t, n, p[N], pre[N]; double ans, MST, e[N][N], g[N][N], x[N], y[N], dis[N]; bool vis[N], used[N][N]; double get_dis(double a, double b, double c, double d) { return sqrt((a - c) * (a - c) + (b - d) * (b - d)); } void prim() { memset(vis, 0, sizeof(vis)); memset(used, 0, sizeof(used)); memset(g, 0, sizeof(g)); for(int i = 1 ; i <= n; ++i ) { dis[i] = e[1][i], pre[i] = 1; } for(int i = 1; i <= n ; ++i) { int u = -1; for(int j = 1; j <= n; ++j) { if(!vis[j] && (u == -1 || dis[j] < dis[u])) u = j; } MST += dis[u]; vis[u] = 1; used[u][pre[u]] = used[pre[u]][u] = 1; for(int j = 1; j <= n; ++j) { if(vis[j] && j != u) g[u][j] = g[j][u] = max(g[j][pre[u]], dis[u]); if(!vis[j] && e[u][j] < dis[j]) dis[j] = e[u][j], pre[j] = u; } } } int main() { scanf("%d", &t); while(t--) { scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%lf%lf%d", &x[i], &y[i], &p[i]); for(int i = 1; i < n; ++i) for(int j = i + 1 ; j <= n; ++j) e[i][j] = e[j][i] = get_dis(x[i], y[i], x[j], y[j]); MST = 0; ans = -1; prim(); for(int i = 1 ; i < n ; ++i ) for(int j = i + 1 ; j <= n ; ++j) if(used[i][j]) ans = max(ans, (p[i] + p[j] ) / (MST - e[i][j])); else ans = max(ans, (p[i] + p[j]) / (MST - g[i][j])); printf("%.2lf ", ans); } return 0; }
线段树
/** /*@author Victor */ #include<bits/stdc++.h> using namespace std; const int N = 200020; struct Note{ int r,l,sum; }tree[N<<2]; //建树 void buildtree(int l,int r, int pos){ tree[pos].l = l; tree[pos].r = r; if(l == r){ scanf("%d",&tree[pos].sum); return ; //某q强调的 } int mid = (l + r) /2; buildtree(l ,mid ,pos<<1);//建立左右子树 buildtree(mid+1,r,pos<<1|1); tree[pos].sum = tree[pos<<1].sum + tree[pos<<1|1].sum; } //结点更新 void update(int p,int c , int pos){ // if(tree[pos].l == tree[pos].r&&tree[pos].l==p){ if(tree[pos].l==tree[pos].r&&tree[pos].l==p){ tree[pos].sum += c; return ; } int mid = (tree[pos].l + tree[pos].r)/2; if(p <= mid) update(p,c,pos<<1);//更新左右节点 else update(p,c,pos<<1|1); tree[pos].sum = tree[pos<<1].sum + tree[pos<<1|1].sum; } // } //点查询 -> query int query(int l ,int r , int pos){ if(tree[pos].l==l&&tree[pos].r==r) return tree[pos].sum; int mid = (tree[pos].l + tree[pos].r)/2; if(r <= mid) return query(l,r,pos*2); else if(l > mid) return query(l,r,pos<<1|1); else return query(l,mid,pos<<1) + query(mid+1,r,pos<<1|1); } int main(){ //freopen("input.txt", "r", stdin); //freopen("output.txt", "w", stdout); int t; scanf("%d",&t); int cas = 1; while(t--){ int n; scanf("%d",&n); printf("Case %d: ",cas++); buildtree(1,n,1); while(1){ char str[20]; scanf("%s",str); if(str[0]=='E') break; else if(str[0]=='Q'){ int t1,t2; scanf("%d%d",&t1,&t2); printf("%d ",query(t1,t2,1)); } else if(str[0]=='S'){ int t1,t2; scanf("%d%d",&t1,&t2); update(t1,-t2,1); } else if(str[0]=='A'){ int t1,t2; scanf("%d%d",&t1,&t2); update(t1,t2,1); } } } return 0; }
线段树
/** /*@author ~victor~ */ #include <bits/stdc++.h> using namespace std; #define maxsize 200020 typedef struct { int left,right; int maxn; }Node; int n,m; int num[maxsize]; Node tree[maxsize*20]; int buildtree(int root,int left,int right)// 构建线段树 { int mid; tree[root].left=left; tree[root].right=right;// 当前节点所表示的区间 if(left==right)// 左右区间相同,则此节点为叶子,max 应储存对应某个学生的值 return tree[root].maxn=num[left]; mid=(left+right)/2; int a,b;// 递归建立左右子树,并从子树中获得最大值 a=buildtree(2*root,left,mid); b=buildtree(2*root+1,mid+1,right); return tree[root].maxn=max(a,b); } int find(int root,int left,int right)// 从节点 root 开始,查找 left 和 right 之间的最大值 { int mid; if(tree[root].left>right||tree[root].right<left)// 若此区间与 root 所管理的区间无交集 return 0; if(left<=tree[root].left&&tree[root].right<=right)// 若此区间包含 root 所管理的区间 return tree[root].maxn; mid=(left+right)/2; int a,b;// 若此区间与 root 所管理的区间部分相交 a=find(2*root,left,right); b=find(2*root+1,left,right); return max(a,b); } int update(int root,int pos,int val)// 更新 pos 点的值 { if(pos<tree[root].left||pos>tree[root].right)// 若 pos 不存在于 root 所管理的区间内 return tree[root].maxn; if(tree[root].left==pos&&tree[root].right==pos)// 若 root 正好是一个符合条件的叶子 return tree[root].maxn=val; int a,b;// 否则。。。。 a=update(2*root,pos,val); b=update(2*root+1,pos,val); tree[root].maxn=max(a,b); return tree[root].maxn; } int main() { char c; int i; int x,y; while(scanf("%d%d",&n,&m)!=EOF) { for(i=1;i<=n;i++) scanf("%d",&num[i]); buildtree(1,1,n); for(i=1;i<=m;i++) { getchar(); scanf("%c%d%d",&c,&x,&y); if(c=='Q') printf("%d ",find(1,x,y)); else { num[x]=y; update(1,x,y); } } } return 0; }
树状数组
/** /*@author victor /* */ #include<bits/stdc++.h> #define lowbit(x) x&-x using namespace std; const int maxn = 5050; int c[maxn]; int n; void add(int i,int x) { while(i <= n) { c[i] += x; i+=lowbit(i); } } int sum(int i) { int tmp = 0; while(i > 0) { tmp += c[i]; i -= lowbit(i); } return tmp; } int a[maxn]; #define _cls(a) memset(a,0,sizeof(a)) int main(){ while(~scanf("%d",&n)){ int ans = 0; _cls(a); _cls(c); for(int i = 1;i <= n; i++) { scanf("%d",&a[i]); a[i]++; ans += sum(n) - sum(a[i]); add(a[i],1); } int min = ans; for(int i = 1 ; i <= n;i++){ ans += n - a[i] -(a[i]-1); if(ans < min) min = ans; } cout << min << ' '; } return 0; }
树状数组add&querry
int main(){ // freopen("out.txt","w",stdout); cin >> n >> q; for(int i = 1 ; i <= n;i++) { int num; scanf("%d",&num); add(bit0,i,num); } while(q--){ char s[5]; int l,r,x; scanf("%s",s); if(s[0]=='C') { scanf("%d%d%d",&l,&r,&x); add(bit0, l, -x*(l-1)); add(bit1, l, x); add(bit0, r+1, x*r); add(bit1, r+1, -x); } else { scanf("%d%d", &l, &r); ll res = 0; res += sum(bit0, r) + sum(bit1, r) * r; res -= sum(bit0, l-1) + sum(bit1, l-1) * (l - 1); printf("%lld ", res); } } return 0; }
线段树
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN = 2e5 + 10; #define lson l,m,i<<1 #define rson m+1,r,i<<1|1 #define length (note[i].r-note[i].l+1) struct Note { int l, r; int mid() { return (l + r) / 2.0; } }; ll ans; Note note[MAXN << 2]; ll add[MAXN << 2]; ll sum[MAXN << 2]; void push_up(int i ) { sum[i] = sum[i << 1] + sum[i << 1 | 1]; } void build(int l, int r, int i) { note[i].l = l; note[i].r = r; sum[i] = 0; add[i] = 0; if(l == r) { sum[i] = 1; return ; } int m = note[i].mid(); build(lson); build(rson); push_up(i); } void push_down(int i, int l) { if(add[i]) { add[i << 1] = add[i]; add[i << 1 | 1] = add[i]; sum[i << 1] = add[i] * (l - (l >> 1)); sum[i << 1 | 1] = add[i] * (l >> 1); add[i] = 0; } } void query(int l, int r, int i) { if(note[i].l == l && note[i].r == r) { ans += sum[i]; return ; } push_down(i, length); int m = note[i].mid(); if(r <= m) query(1, r, i << 1); else if(l > m) query(l, r, i << 1 | 1); else { query(lson); query(rson); } } void update(int l, int r, int i, int v) { if(note[i].l == l && note[i].r == r) { sum[i] = (ll)v * (r - l + 1); add[i] = v; return ; } push_down(i, length); int m = note[i].mid(); if(r <= m) { update(l, r, i << 1, v); } else { if(l > m) update(l, r, i << 1 | 1, v); else { update(lson, v); update(rson, v); } } push_up(i); } int main() { int n, c, d, i, t, m, j, e; scanf("%d", &t); for(int i = 1; i <= t; i ++) { scanf("%d", &n); build(1, n, 1); scanf("%d", &m); for(j = 1; j <= m; j++) { scanf("%d%d%d", &c, &d, &e); update(c, d, 1, e); } ans = 0; query(1, n, 1); printf("Case %d: The total value of the hook is %d. ", i, ans); } return 0; }
RMQ&LCA
#include <iostream> #include <cstring> #include <string> #include <vector> #include <queue> #include <cstdio> #define INF 0x3f3f3f3f #define MAXN 200005 using namespace std; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 2000000 + 7; int MAX[maxn << 2], MIN[maxn << 2]; void PushUp(int rt) { MAX[rt] = max(MAX[rt << 1], MAX[rt << 1 | 1]); MIN[rt] = min(MIN[rt << 1], MIN[rt << 1 | 1]); } void build(int l, int r, int rt) { if(r == l) { scanf("%d", &MAX[rt]); MIN[rt] = MAX[rt]; return; } int m = (l + r) >> 1; build(lson); build(rson); PushUp(rt); } int querymax(int L, int R, int l, int r, int rt) { if(L <= l && r <= R) { return MAX[rt]; } int m = (l + r) >> 1; int ret = 0; if(L <= m) ret = max(ret, querymax(L, R, lson)); if(R > m) ret = max(ret, querymax(L, R, rson)); return ret; } int querymin(int L, int R, int l, int r, int rt) { if(L <= l && r <= R) { return MIN[rt]; } int m = (l + r) >> 1; int ret = INF; if(L <= m) ret = min(ret, querymin(L, R, lson)); if(R > m) ret = min(ret, querymin(L, R, rson)); return ret; } int main() { int n, t; while(~scanf("%d%d", &n, &t)) { build(1, n, 1); while(t--) { int l, r; scanf("%d%d", &l, &r); printf("%d ", querymax(l, r, 1, n, 1) - querymin(l, r, 1, n, 1)); } } return 0; }
简单DP
策略,转移,具体补充