图论算法
存图
//邻接表
int h[N],w[N],e[N],ne[N],idx;
void add(int a,int b,int c){
e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
for(int i = h[u]; i != 01; i = ne[i]){
int j = e[i];
int value = w[i];
}
//无边权
const int N=1e5+10;
vector<int> G[N];
void addedge(int u,int v){
G[u].push_back(v);
G[v].push_back(u);
}
//有边权
vector<pair<int,int> > G[N];
void addedge(int u,int v,int w){
G[u].push_back(make_pair(v,w));
G[v].push_back(make_pair(u,w));
}
int to = G[i].first(),w = G[i].second();
******************************************************************
//去重
//链式前向星存图
struct node{
int to,next,w;
}edges[maxn];
int cnt = 0,head[maxn],mp[maxn][maxn];
void add(int u,int v,int w){
edges[++cnt].to = v;
edges[cnt].next = head[u];
edges[cnt].w = w;
head[u] = cnt;
}
for(int i = 1,x,y,w; i <= m; i++){
cin >> x >> y >> w;
if(mp[x][y]){
edges[mp[a][b]].w = min(edges[mp[a][b]].w,c);
}else{
add(a,b,c);
mp[a][b] = cnt;
}
}
//set去重用法和使用vector一样push_back()换成insert()
set<pair<int,int> > G[maxn]; //只能用迭代器进行遍历
for(int i = 1,x,y,z; i <= m; i++){
cin >> x >> y >> z;
G[x].insert(make_pair(y,z));
}
BFS
//vector存储
#include <bits/stdcpp.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10,maxm = 2e6+10;
const int INF = 0x3f3f3f3f,mod = 100003;
vector<int> g[maxm];
int dep[maxn],vis[maxn],num[maxn];
int main(){
int n,m; cin >> n >> m;
for(int i = 1,x,y; i <= m; i++){
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
dep[1] = 1,vis[1] = 1,num[1] = 1;
queue<int> q;
q.push(1);
while(!q.empty()){
int t = q.front();
q.pop();
for(int i = 0; i < (int)g[t].size(); i++){
int x = g[t][i];
if(!vis[x]){
dep[x] = dep[t]+1;
vis[x] = 1;
q.push(x);
}
if(dep[x] == dep[t]+1){
num[x] = (num[x]+num[t])%mod;
}
}
}
for(int i = 1; i <= n; i ++){
cout << num[i] << endl;
}
return 0;
}
Dijkstra
//链式前向星存
#include <bits/stdcpp.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
const int INF = 0x3f3f3f3f;
struct node1{
int num; //编号
int d; //从起点到该点的最短路程
bool operator < (const node1 &a) const {
return d>a.d;//最小值优先
}
};
struct node2{
int to,w,next;
}edge[maxn];
int cnt = 0,head[maxn];
int n,m,s,dis[maxn],vis[maxn];
void add(int u,int v,int w){
edge[++cnt].to = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
void Dijkstra(int s){
for(int i = 1; i <= n; i++)
dis[i] = INF;
dis[s] = 0;
priority_queue<node1> q;
q.push((node1){s,dis[s]});
while(!q.empty()){
int u = q.top().num;
q.pop();
if(vis[u])continue;
vis[u] = true;
for(int i = head[u]; i ; i = edge[i].next){
int t = edge[i].to;
if(dis[u]+edge[i].w < dis[t]){
dis[t] = dis[u]+edge[i].w;
q.push((node1){t,dis[t]});
}
}
}
}
int main(){
cin >> n >> m >> s;
for(int i = 1, a,b,c; i <= m; i++){
cin >> a >> b >> c;
add(a,b,c);
}
Dijkstra(s);
for(int i = 1; i <= n; i++)cout << dis[i] << ' ';
return 0;
}
弗洛伊德
//多源最短路
#include <bits/stdcpp.h>
using namespace std;
const int N =1e5+10;
const int INF = 1e9;
int n,m,k;
int g[210][210];
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(i==j)g[i][j] = 0;
else g[i][j] = INF;
for(int i = 1; i <= m; i++){
int x,y,w; cin >> x >> y >> w;
g[x][y] = min(g[x][y],w);
}
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
g[i][j] = min(g[i][j],g[i][k]+g[k][j]);
while(k--){
int a,b;scanf("%d%d",&a,&b);
if(g[a][b] > INF/2)puts("impossible");
else printf("%d\n",g[a][b]);
}
return 0;
}
Bellman_Ford
善于求解有边数限制的最短路求法
#include <bits/stdcpp.h>
using namespace std;
const int maxn=1e4+10;
int n,m,k,dis[510],last[510]; //last数组是存储上一次松弛的dis数组
struct Edge{
int x,y,w;
}edges[maxn];
void bellman_ford(){
memset(dis,0x3f,sizeof dis);
dis[1] = 0;
for(int i = 1; i <= k; i++){ //进行k次松弛,就相当于走k条边
memcpy(last,dis,sizeof dis);
for(int j = 1; j <= m; j++){
auto e = edges[j];
dis[e.y] = min(dis[e.y],last[e.x]+e.w); //和dijkstra差不多
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i = 1; i <= m; i++){
int a,b,c;scanf("%d%d%d",&a,&b,&c);
edges[i] = {a,b,c};
}
bellman_ford();
if(dis[n] > 0x3f3f3f3f / 2)puts("impossible");
else printf("%d\n",dis[n]);
return 0;
}
SPFA
//没有优化
#include <bits/stdcpp.h>
using namespace std;
const int N =1e5+10;
int h[N],w[N],e[N],ne[N],idx;
int n,m,dis[N];
bool st[N];
void add(int a,int b,int c){
e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
void spfa(){
memset(dis,0x3f,sizeof dis);
dis[1] = 0,st[1] = true;
queue<int> q;
q.push(1);
while(!q.empty()){
int t = q.front();
q.pop();
st[t] = false;
for(int i = h[t]; i != -1; i = ne[i]){
int j = e[i];
if(dis[j] > dis[t]+w[i]){
dis[j] = dis[t]+w[i];
if(!st[j]){
q.push(j);
st[j] = true;
}
}
}
}
}
int main(){
memset(h,-1,sizeof h);
scanf("%d%d",&n,&m);
for(int i = 1; i <= m; i++){
int a,b,c; scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
spfa();
if(dis[n]==0x3f3f3f3f)puts("impossible");
else printf("%d\n",dis[n]);
return 0;
}
//被双端队列优化过的spfa
#include <bits/stdcpp.h>
using namespace std;
const int maxn=1e5+10,maxm=2e5+10,INF = 0x3f3f3f3f;
struct node{
int to,w,next;
}edge[maxm];
int n,m,s,head[maxn],dis[maxn],cnt = 0,vis[maxn];
void add(int u,int v,int w){
edge[++cnt].to = v,edge[cnt].w = w,edge[cnt].next = head[u],head[u] = cnt;
}
void spfa(){
for(int i = 1; i <= n; i++){
vis[i] = 0;
dis[i] = INF;
}
deque<int> Q;
Q.push_back(s);
vis[s]=1;dis[s]=0;
while(!Q.empty()){
int t=Q.front();
Q.pop_front();
vis[t]=0;
for(int i=head[t]; i ;i=edge[i].next){
int u=edge[i].to;
if(dis[u]>dis[t]+edge[i].w){
dis[u]=dis[t]+edge[i].w;
if(!vis[u]){
if(!Q.empty()&&dis[u]<dis[Q.front()])Q.push_front(u);
else Q.push_back(u);
vis[u]=1;
}
}
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i = 0, u, v, w;i < m; i++){
cin>>u>>v>>w;
add(u,v,w);
}
spfa();
for(int i = 1;i <= n;i++)
cout<<dis[i]<<endl;
return 0;
}
SPFA判断负环
#include <bits/stdcpp.h>
using namespace std;
const int N =1e5+10;
int h[N],w[N],e[N],ne[N],idx;
int n,m,dis[N],cnt[N];
bool st[N];
void add(int a,int b,int c){
e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
bool spfa(){
queue<int> q;
for(int i = 1; i <= n; i++){ //因为判断从每个点开始是否存在负环,不单单只是从一号点
st[i] = true;
q.push(i);
}
while(!q.empty()){
int t = q.front();
q.pop();st[t] = false;
for(int i = h[t]; i!=-1; i = ne[i]){
int j = e[i];
if(dis[j] > dis[t] + w[i]){
dis[j] = dis[t] + w[i];
cnt[j] = cnt[t] + 1;
if(cnt[j]>=n)return true;
if(!st[j]){
q.push(j);
st[j] = true;
}
}
}
}
return false;
}
int main(){
memset(h,-1,sizeof h);
scanf("%d%d",&n,&m);
for(int i = 1; i <= m; i++){
int a,b,c; scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
if(spfa())puts("Yes");
else puts("No");
return 0;
}
Prim
#include <bits/stdcpp.h>
using namespace std;
const int N = 510, INF = 0x3f3f3f3f,maxn = 2e5+10;
int n, m;
bool st[N];
int h[maxn],w[maxn],e[maxn],ne[maxn],idx;
struct node{
int ip;
int value;
bool operator < (const node &a)const {
return value > a.value;
}
};
void add(int a,int b,int c){
e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
int prim(){
int res = 0,cnt = 0;
priority_queue<node> q;
q.push({1,0});
while(q.size()){
auto t = q.top();
q.pop();
if(st[t.ip]) continue;
st[t.ip] = true,res += t.value,cnt++;
for(int i = h[t.ip]; i != -1; i = ne[i]){
int j = e[i];
if(!st[j]){
q.push({j,w[i]});
}
}
}
if(cnt != n)return INF;
return res;
}
int main()
{
scanf("%d%d", &n, &m);
memset(h,-1,sizeof h);
for(int i = 1; i <= m; i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
int t = prim();
if(t==INF)puts("impossible");
else printf("%d\n",t);
return 0;
}
拓扑排序
#include <bits/stdcpp.h>
using namespace std;
const int maxn=1e5+10;
vector<int> G[maxn];
vector<int> g;
queue<int> q;
int num[maxn];//记录入度
int n,m;
int find(){
int cnt = 0;
for(int i=1; i<=n; i++){
if(num[i]==0){
g.push_back(i);
q.push(i);
cnt++;
}
}
while(q.size()){
int t = q.front();
q.pop();
for(auto u : G[t]){
num[u]--;
if(num[u]==0){
g.push_back(u);
cnt++;
q.push(u);
}
}
}
if(cnt==n)return 1;
else return 0;
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1; i <= m; i++){
int x,y;scanf("%d%d",&x,&y);
G[x].push_back(y);
num[y]++;
}
if(!find())cout << -1 << endl;
else {
for(auto x:g)cout << x << ' ';
cout << endl;
}
return 0;
}
并查集
普通写法:
int find(int a){
int r = a;
while(f[r] != r) //寻找根结点
r = f[r];
return r;
}
递归写法:
//递归形式的路径压缩
int find(int x){
return f[x]==x?x:(f[x]=find(f[x]));
}
迭代写法:
//迭代形式的路径压缩
int find(int a){
int p = a, t;
while (f[p] != p) p = f[p];//跟递归不同的是,通过循环先找到祖先p,然后再接着改结点
while (a!= p){
t = f[a];
f[a] = p;
a = t;
}
return a;
}
合并:
void memge(int x,int y){
x = find(x);
y = find(y);
if(x!=y)f[y] = x;
}
带权值的并查集
#include <bits/stdcpp.h>
using namespace std;
const int N = 50010;
int n, m;
int p[N], d[N]; //p[x]表示关系,d[x]表示到根的权值
int find(int x) {
if (p[x] != x) {
int t = find(p[x]); //找根
d[x] += d[p[x]]; //更新距离
p[x] = t; //指向根
}
return p[x];
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) p[i] = i;
int res = 0;
while (m -- ){
int t, x, y;
scanf("%d%d%d", &t, &x, &y);
if (x > n || y > n) res ++ ;
else{
int px = find(x), py = find(y);
if (t == 1) { //x和y是同类
if (px == py && (d[x] - d[y]) % 3) res ++ ; //如果d[x]=d[y]说明距离相等
else if (px != py) { //更新
p[px] = py;
d[px] = d[y] - d[x]; //(d[x]+?-d[y])%3==0
}
}else { //x和y不是同类
if (px == py && (d[x] - d[y] - 1) % 3) res ++ ;
else if (px != py) {
p[px] = py;
d[px] = d[y] + 1 - d[x]; //(d[x]+?-d[y]-1)%3==0
}
}
}
}
printf("%d\n", res);
return 0;
}
全员最短路
1.不带负权边的
直接来n次优化过的Dijkstra求时间复杂度为O(n*m*log(n))
不能用Floyd,O(n^3)太慢了
2.带负权边的
单纯的Dijkstra已经不行了
用SPFA求出一个上帝视角的h[maxn]数组:从一个源点S到每一个点的最短路,且S到每个点已知距离为0
然后再求n此优化过的Dijkstra
#include<bits/stdcpp.h>
using namespace std;
typedef long long ll;
const ll INF = 1e9;
const int maxn = 6e3 + 5;
struct node{
ll dis, id;
inline bool operator < (const node &x)const{
return dis > x.dis;
}
node (int x, int y) { dis = x, id = y; }
};
int n, m, in[maxn],vis[maxn];
ll h[maxn], dis[maxn];
vector<pair<int, ll> > G[maxn];
bool SPFA(int s) { //从源点开始求一遍最短路 , 记录在h数组中
queue <int> q;
memset(h,0x3f,sizeof(h));
h[s] = 0;
vis[s] = 1;
q.push(s);
while(!q.empty()) {
int u = q.front();
q.pop();
vis[u] = 0;
for(int i=0; i<(int)G[u].size(); i++) {
int v = G[u][i].first;
ll w = G[u][i].second;
if(h[v] > h[u]+w) {
h[v] = h[u] + w;
if(++in[v] >= n) return true;
if(!vis[v]) {
vis[v] = 1;
q.push(v);
}
}
}
} return false;
}
void dijkstra(int s) {
priority_queue<node> q;
memset(vis, 0, sizeof(vis));
for(int i=1; i<=n; i++) dis[i] = (i==s) ? 0 : (INF);
q.push(node(0, s));
while(!q.empty()) {
int u = q.top().id;
q.pop();
if(vis[u])continue;
vis[u] = 1;
for(int i=0; i<(int)G[u].size(); i++) {
int v = G[u][i].first;
ll w = G[u][i].second;
if(dis[v] > dis[u]+w) {
dis[v] = dis[u] + w;
if(!vis[v]) q.push(node(dis[v], v));
}
}
}
}
int main() {
scanf("%d%d", &n, &m);
while(m--) {
int u, v;
ll w;
scanf("%d%d%lld", &u, &v, &w);
G[u].push_back(make_pair(v, w));
}
for(int i=1; i<=n; i++) G[0].push_back(make_pair(i, 0*1ll));
if(SPFA(0)){
printf("-1\n");
return 0;
}
for(int u=1; u<=n; u++)
for(int i=0; i<(int)G[u].size(); i++)
G[u][i].second += h[u] - h[G[u][i].first]; //u -> v: W(u,v) = w(u,v)+h[u]-h[v];
for(int i=1; i<=n; i++) {
dijkstra(i); ll ans = 0;
for(ll j=1; j<=n; j++)
ans += (dis[j]==INF) ? (j*INF) : (j*(dis[j]+h[j]-h[i]));// u -> v: w(u,v) = W[u,v] + h[v]-h[u];与上面与之对应
printf("%lld\n", ans);
}
return 0;
}
N皇后
#include<cstdio>
#include<iostream>
using namespace std;
int ans[14],check[3][28]={0},sum=0,n;
void dfs(int num){
if(num>n){
sum++;
if(sum>3) return;
else{
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
printf("\n");
return;
}
}
for(int i=1;i<=n;i++){
if((!check[0][i])&&(!check[1][num+i])&&(!check[2][num-i+n])){
ans[num]=i;
check[0][i]=1; check[1][num+i]=1; check[2][num-i+n]=1;
dfs(num+1);
check[0][i]=0; check[1][num+i]=0; check[2][num-i+n]=0;
}
}
}
int main(){
scanf("%d",&n);
dfs(1);
printf("%d",sum);
return 0;
}
二分图的判断
#include <bits/stdcpp.h>
using namespace std;
const int N = 2e5+10; //数组要开二倍大小,因为是无向图
int n,m,st[N];
int h[N],e[N],ne[N],idx;
void add(int a,int b){
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
bool dfs(int x,int t){
st[x] = t;
for(int i = h[x]; i != -1; i = ne[i]){
int j = e[i];
if(!st[j]){
if(!dfs(j,3-t))return false; //没有被染色,那就去染色
}else if(st[j]==t)return false; //如果发生染色冲突,那就直接结束
}
return true;
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);
for(int i = 1; i <= m; i++){
int a,b;scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
bool f = true;
for(int i = 1; i <= n; i++){ //因为不能保证整个图都是连通的,所以都要遍历
if(!st[i]){
if(!dfs(i,1)){
f = false;
break;
}
}
}
if(f)puts("Yes");
else puts("No");
return 0;
}
二分图最大匹配(匈牙利算法)
#include <bits/stdcpp.h>
using namespace std;
const int N = 1e5+10;
int n1,n2,m,match[N]; //match数组是记录右边端点的匹配情况
bool st[N]; //标记有哪些被访问过了
int h[N],e[N],ne[N],idx;
void add(int a,int b){
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
bool find(int x){
for(int i = h[x]; i != -1; i = ne[i]){
int j = e[i];
if(!st[j]){
st[j] = true;
if(match[j] == 0||find(match[j])){ //1.没有被匹配 2.被匹配了,但是被匹配的结点有其他选择
match[j] = x;
return true;
}
}
}
return false; //没有一个匹配的
}
int main()
{
scanf("%d%d%d",&n1,&n2,&m);
memset(h,-1,sizeof h);
for (int i = 1; i <= m; i++){
int a,b;scanf("%d%d",&a,&b);
add(a,b);
}
int res = 0;
for(int i = 1; i <= n1; i++){ //从左边开始匹配
memset(st,false,sizeof st); //每次匹配都要尝试右边的每一个结点
if(find(i))res++;
}
printf("%d",res);
return 0;
}
树的直径
//无向无边权图
//法一:两次dfs,第一次求出最远点,第二次以最远点为起点,再次求出的最远点对应的就是树的直径
#include <bits/stdcpp.h>
using namespace std;
const int maxn = 1e4+10;
vector<int> g[maxn];
int dis[maxn],ans,pos;
void dfs(int x,int father){
if(ans <= dis[x]){
ans = dis[x];
pos = x;
}
int le = g[x].size();
for(int i = 0; i < le; i++){
int to = g[x][i];
if(to==father)continue;
dis[to] = dis[x]+1;
dfs(to,x);
}
}
int main(){
int n; cin >> n;
int x,y;
for(int i = 1; i < n; i++){
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
ans = 0;
dis[x] = 0;
dfs(x,0);
ans = 0;
dis[pos] = 0;
dfs(pos,0);
cout << ans << endl;
return 0;
}
//树形dp写法
#include <bits/stdcpp.h>
using namespace std;
const int maxn = 1e4+10;
vector<int> g[maxn];
int f1[maxn],f2[maxn],ans,n,m;
void dp(int x,int father){
for(int i = 0,j; i < (int)g[x].size(); i++){
j = g[x][i];
if(j==father)
continue;
dp(j,x);
if(f1[x] < f1[j] + 1){ ///f1[j]表示j到叶子节点距离的最大值
f2[x] = f1[x]; ///f2[x]表示x到叶子节点距离的次大值
f1[x] = f1[j] + 1;
}else if(f2[x] < f1[j] + 1) ///这个就是判断节点x到次远点的更新
f2[x] = f1[j] + 1;
ans=max(ans,f1[x] + f2[x]);
}
}
int main(){
scanf("%d",&n);
for(int i = 1,x,y;i < n; i++){
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
dp(1,0);
printf("%d",ans);
return 0;
}
//对于无向有权图,两遍bfs,和dfs一样的原理
#include <bits/stdcpp.h>
using namespace std;
const int N = 1e5+10;
vector<pair<int,int> > G[N];
int n,pos,vis[N],ans;
struct node{
int d;
int step;
};
void addedge(int u,int v,int w){
G[u].push_back(make_pair(v,w));
G[v].push_back(make_pair(u,w));
}
void bfs(int s){
queue<node> q;
q.push((node){s,0});
int ma = -1;
while(!q.empty()){
node t = q.front();
q.pop();
for(int v = 0; v < (int)G[t.d].size(); v++){
int to = G[t.d][v].first;
if(vis[to])continue;
vis[to] = 1;
int w = G[t.d][v].second;
if(w+t.step>ma){
ma = w+t.step;
pos = to;
}
q.push((node){to,w+t.step});
}
}
ans=ma;
}
int main(){
cin >> n;
for(int i = 1; i < n; i++){
int x,y,t;cin >> x >> y >> t;
addedge(x,y,t) ;
}
vis[1] = 1;
bfs(1);
memset(vis,0,sizeof vis);
vis[pos] = 1;
bfs(pos);
cout << ans << endl;
return 0;
}
树的重心
//树的重心:以某个点为根,它的最大的子树最小
#include <bits/stdcpp.h>
using namespace std;
const int maxn = 1e5+10;
int n,father[maxn],sz[maxn],ans,num,res;
vector<int> g[maxn];
int dfs(int u,int fa){ //给每个点附上权值
father[u] = fa;
sz[u] = 1;
for(int v = 0; v < g[u].size(); ++v){
if(g[u][v]!=fa)
sz[u]+=dfs(g[u][v],u);
}
return sz[u];
}
void dfs_n(int u,int fa,int step){ //从重心到每个节点的距离之和
for(int v = 0; v < g[u].size(); ++v){
if(g[u][v]!=fa){
dfs_n(g[u][v],u,step+1);
}
}
num+=step;
}
int main(){
cin >> n;
for(int i = 1,x,y; i < n; i++){
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1,0);
int mi = maxn+10; //mi存的是最大值的最小值
for(int u = 1; u <= n; ++u){
int ma = n - sz[u]; //ma以每个点为重心的最大子树值,n - sz[u]是指父亲那棵子树的值也要考虑到
int f;
for(int v = 0; v < g[u].size(); ++v){
if(g[u][v]!=father[u])
ma = max(ma,sz[g[u][v]]);
}
if(ma < mi){ //更新
mi = ma;
ans = u;
res = ma;
}
}
cout << res << endl;
//dfs_n(ans,0,0);
//cout << ans << ' ' << num << endl;
return 0;
}
树上最远距离
//求树上每个点能到达的最远距离,hdu2196
/*两次dfs
第一次记录从下向上最长距离
第二次记录从父节点到儿子节点最长距离*/
#include <bits/stdcpp.h>
using namespace std;
const int maxn = 1e4+10;
vector<pair<int,int> > G[maxn];
int f[maxn],g[maxn],p[maxn];
void init(int n){
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
memset(p,0,sizeof(p));
for(int i = 1; i <= n; ++i)G[i].clear();
}
int dfs1(int u,int pre){
for(int i = 0; i < (int)G[u].size(); ++ i){
if(G[u][i].first!=pre){
f[u] = max(f[u],G[u][i].second+dfs1(G[u][i].first,p[G[u][i].first] = u));
}
}
return f[u];
}
void dfs2(int u,int pre){
int t = 0;
g[u] = g[pre];
for(int i = 0; i < (int)G[pre].size(); ++ i){
if(G[pre][i].first == p[pre])continue;
if(G[pre][i].first == u)t = G[pre][i].second;
else g[u] = max(g[u],f[G[pre][i].first]+G[pre][i].second);
}
g[u]+=t;
for(int i = 0; i < (int)G[u].size(); ++ i){
if(G[u][i].first != pre)dfs2(G[u][i].first,u);
}
}
int main(){
int n;
while(~scanf("%d",&n)){
init(n);
for(int i = 2,x,y; i <= n; ++i){
cin >> x >> y;
G[i].push_back(make_pair(x,y));
G[x].push_back(make_pair(i,y));
}
dfs1(1,0);
dfs2(1,0);
for(int i = 1; i <= n; i++){
cout << max(f[i],g[i]) << endl;
}
}
return 0;
}
分层图最短路
#include <bits/stdcpp.h>
#define ll long long
#define inf 0x7f7f7f7f
const int maxn = 5e5+10;
using namespace std;
struct node{
int to,w,next;
}edge[maxn];
struct Node{
int dis,id;
Node(const int a,const int b){
dis = a;
id = b;
}
bool operator < (const Node& a)const {
return dis > a.dis;
}
};
int head[maxn], cnt;
int dis[maxn], vis[maxn];
int n, m, t, k;
void init(){
memset(head,0,sizeof(head));
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
cnt = 0;
}
void add(int u,int v,int w){
edge[++cnt].to = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
void dijkstra(){
priority_queue<Node> q;
dis[1] = 0; q.push(Node(dis[1],1));
while(!q.empty()){
int now = q.top().id;
q.pop();
if(vis[now]) continue;
vis[now] = 1;
for(int i = head[now]; i ; i = edge[i].next){
int v = edge[i].to;
if(!vis[v]&&dis[v] > dis[now] + edge[i].w){
dis[v] = dis[now] + edge[i].w;
q.push(Node(dis[v],v));
}
}
}
}
int main(){
while(~scanf("%d%d%d", &n, &m, &k)){
init();
while(m--){
int u, v, w;
scanf("%d%d%d",&u, &v, &w);
for(int i = 0; i <= k; i++){
add(u + i * n, v + i * n, w);
add(v + i * n, u + i * n, w);
if(i != k){
add(u + i * n, v + (i + 1) * n, w*0.5);
add(v + i * n, u + (i + 1) * n, w*0.5);
}
}
}
dijkstra();
int ans = inf;
for(int i = 0; i <= k; i++)
ans = min(ans, dis[n + i * n]);
printf("%d\n",ans);
}
return 0;
}
最短路径数
#include <bits/stdcpp.h>
using namespace std;
typedef long long ll;
const int maxn = 2e3+10;
const int INF = 0x3f3f3f3f;
set<pair<int,int> > G[maxn];
ll dis[maxn],vis[maxn],cnt[maxn];
struct node{
int num; //编号
int d; //从起点到该点的最短路程
bool operator < (const node &a) const {
return d>a.d;
}
};
int main(){
int n,m; cin >> n >> m;
for(int i = 1,x,y,z; i <= m; i++){
cin >> x >> y >> z;
G[x].insert(make_pair(y,z));
}
for(int i = 1; i <= n; i ++)dis[i] = INF;
dis[1] = 0,cnt[1] = 1;
priority_queue<node> q;
q.push((node){1,0});
while(!q.empty()){
int t = q.top().num;
q.pop();
if(vis[t])continue;
vis[t] = 1;
for(auto i = G[t].begin(); i!=G[t].end(); i ++){
int x = i->first;
int w = i->second;
if(dis[x]==dis[t]+w){
cnt[x] += cnt[t];
}else if(dis[t]+w<dis[x]){
dis[x] = dis[t]+w;
cnt[x] = cnt[t];
q.push((node){x,dis[x]});
}
}
}
if(dis[n]==INF){
cout << "No answer" << endl;
return 0;
}
cout << dis[n] << ' ' << cnt[n] << endl;
return 0;
}
最小覆盖圆
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct Point{
double x,y;
};
struct Point a[1005],d;
double r;
double get_dis(Point p1,Point p2){ //两点间距离
return (sqrt((p1.x-p2.x)*(p1.x -p2.x)+(p1.y-p2.y)*(p1.y-p2.y)));
}
double get_muti(Point p1, Point p2,Point p0){
return ((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y));
}
void get_o(Point p,Point q,int n){
d.x=(p.x+q.x)/2.0;
d.y=(p.y+q.y)/2.0;
r=get_dis(p,q)/2;
int k;
double c1,c2,t1,t2,t3;
for(k=1;k<=n;k++) {
if(get_dis(d,a[k])<=r)continue;
if(get_muti(p,q,a[k])!=0.0) {
c1=(p.x*p.x+p.y*p.y-q.x*q.x-q.y*q.y)/2.0;
c2=(p.x*p.x+p.y*p.y-a[k].x*a[k].x-a[k].y*a[k].y)/2.0;
d.x=(c1*(p.y-a[k].y)-c2*(p.y-q.y))/((p.x-q.x)*(p.y-a[k].y)-(p.x-a[k].x)*(p.y-q.y));
d.y=(c1*(p.x-a[k].x)-c2*(p.x-q.x))/((p.y-q.y)*(p.x-a[k].x)-(p.y-a[k].y)*(p.x-q.x));
r=get_dis(d,a[k]);
}
else {
t1=get_dis(p,q);
t2=get_dis(q,a[k]);
t3=get_dis(p,a[k]);
if(t1>=t2&&t1>=t3) {
d.x=(p.x+q.x)/2.0;
d.y=(p.y+q.y)/2.0;r=get_dis(p,q)/2.0;
}else if(t2>=t1&&t2>=t3) {
d.x=(a[k].x+q.x)/2.0;
d.y=(a[k].y+q.y)/2.0;
r=get_dis(a[k],q)/2.0;
}else {
d.x=(a[k].x+p.x)/2.0;
d.y=(a[k].y+p.y)/2.0;
r=get_dis(a[k],p)/2.0;
}
}
}
}
void solve(Point pi,int n){
d.x=(pi.x+a[1].x)/2.0;
d.y=(pi.y+a[1].y)/2.0;
r=get_dis(pi,a[1])/2.0;
int j;
for(j=2;j<=n;j++){
if(get_dis(d,a[j])<=r)continue;
else get_o(pi,a[j],j-1);
}
}
int main(){
int i,n;
while(scanf("%d",&n)&&n){
for(i=1;i<=n;i++){
scanf("%lf %lf",&a[i].x,&a[i].y);
}
random_shuffle(a+1,a+n+1);
if(n==1){ printf("%.2lf %.2lf 0.00\n",a[1].x,a[1].y);continue;}
r=get_dis(a[1],a[2])/2.0;
d.x=(a[1].x+a[2].x)/2.0;
d.y=(a[1].y+a[2].y)/2.0;
for(i=3;i<=n;i++){
if(get_dis(d,a[i])<=r)continue;
else
solve(a[i],i-1);
}
printf("%.2lf %.2lf %.2lf\n",d.x,d.y,r);
}
return 0;
}
难点算法
严格次小生成树
#include <bits/stdcpp.h>
using namespace std;
const int N=1e5+10;
//前向星存边
struct Edge{
int to,from,next,val;
bool isin;
Edge(){
isin=val=next=0;
}
bool operator < (const Edge &A)const {
return val<A.val;
}
}e[N<<1],E[N*3];
int len,Head[N];
void Ins(int a,int b,int c){
e[++len].to=b;e[len].val=c;
e[len].next=Head[a];Head[a]=len;
}
//并查集+Kurskal
int f[N],m,n;
int find(int x){
return f[x]==x?x:(f[x]=find(f[x]));
}
int ans=0x3f3f3f3f;long long tot=0;
void Krs(){
int cnt=1;
sort(E+1,E+m+1);
for(int i=1;cnt<n;i++){
int v=E[i].to,u=E[i].from;
if(find(v)!=find(u)){
cnt++;
E[i].isin=1;
tot+=E[i].val;//计算最小生成树
Ins(u,v,E[i].val);
Ins(v,u,E[i].val);
f[find(v)]=find(u);
}
}
}
//倍增lca板子
int dep[N],p[N][20],Max[N][20],Smax[N][20];
void dfs(int x){
for(int i=0;p[x][i];i++){
p[x][i+1]=p[p[x][i]][i];
Max[x][i+1]=max(Max[x][i],Max[p[x][i]][i]);
//注意求次大的时候看看两段的最大值是不是相等,如果不判断的话
//在最大值相等的时候,次大值会被更新为最大值
if(Max[x][i]==Max[p[x][i]][i])
Smax[x][i+1]=max(Smax[x][i],Smax[p[x][i]][i]);
else
Smax[x][i+1]=max(min(Max[x][i],Max[p[x][i]][i]),
max(Smax[x][i],Smax[p[x][i]][i]));
}
for(int i=Head[x];i;i=e[i].next){
int v=e[i].to;
if(v!=p[x][0]){
dep[v]=dep[x]+1;
Max[v][0]=e[i].val;
Smax[v][0]=-1;
p[v][0]=x;
dfs(v);
}
}
}
//这段和lca板子一模一样
int lca(int a,int b){
if(dep[a]<dep[b])swap(a,b);
int d=dep[a]-dep[b];
for(int i=0;d;i++,d>>=1)
if(d&1)a=p[a][i];
if(a==b)return a;
for(int i=18;i>=0;i--)
if(p[a][i]!=p[b][i])
a=p[a][i],b=p[b][i];
return p[a][0];
}
//计算
void calc(int u,int v,int w){
int d=dep[u]-dep[v];//深度差,判断路径
int m1=0,m2=0;
for(int i=0;d;i++,d>>=1){
if(d&1){//如果可以往上跳
m2=max(m2,Smax[u][i]);//细节,先求次大值
if(Max[u][i]>m1){//如果更新最大值,那么原来的最大值m1可能为新的次大值
m2=max(m2,m1);//判断次大值是否更新
m1=Max[u][i];//更新最大值
}
}
}
if(m1==w)ans=min(ans,w-m2);//如果最大值与边相等,用次大值更新
else ans=min(ans,w-m1);//否则用最大值
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
f[i]=i;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&E[i].from,&E[i].to,&E[i].val);
Krs();
dfs(1);
for(int i=1;i<=m;i++){
if(!E[i].isin){
int u=E[i].from,v=E[i].to,Lca;
Lca=lca(u,v);
calc(u,Lca,E[i].val);
calc(v,Lca,E[i].val);
}
}
printf("%lld\n",tot+ans);
}
LCA
#include <bits/stdcpp.h>
using namespace std;
const int N=1e5+10;
//前向星存边
struct Edge{
int to,from,next,val;
bool isin;
Edge(){
isin=val=next=0;
}
bool operator < (const Edge &A)const {
return val<A.val;
}
}e[N<<1],E[N*3];
int len,Head[N];
void Ins(int a,int b,int c){
e[++len].to=b;e[len].val=c;
e[len].next=Head[a];Head[a]=len;
}
//并查集+Kurskal
int f[N],m,n;
int find(int x){
return f[x]==x?x:(f[x]=find(f[x]));
}
int ans=0x3f3f3f3f;long long tot=0;
void Krs(){
int cnt=1;
sort(E+1,E+m+1);
for(int i=1;cnt<n;i++){
int v=E[i].to,u=E[i].from;
if(find(v)!=find(u)){
cnt++;
E[i].isin=1;
tot+=E[i].val;//计算最小生成树
Ins(u,v,E[i].val);
Ins(v,u,E[i].val);
f[find(v)]=find(u);
}
}
}
//倍增lca板子
int dep[N],p[N][20],Max[N][20],Smax[N][20];
void dfs(int x){
for(int i=0;p[x][i];i++){
p[x][i+1]=p[p[x][i]][i];
Max[x][i+1]=max(Max[x][i],Max[p[x][i]][i]);
//注意求次大的时候看看两段的最大值是不是相等,如果不判断的话
//在最大值相等的时候,次大值会被更新为最大值
if(Max[x][i]==Max[p[x][i]][i])
Smax[x][i+1]=max(Smax[x][i],Smax[p[x][i]][i]);
else
Smax[x][i+1]=max(min(Max[x][i],Max[p[x][i]][i]),
max(Smax[x][i],Smax[p[x][i]][i]));
}
for(int i=Head[x];i;i=e[i].next){
int v=e[i].to;
if(v!=p[x][0]){
dep[v]=dep[x]+1;
Max[v][0]=e[i].val;
Smax[v][0]=-1;
p[v][0]=x;
dfs(v);
}
}
}
//这段和lca板子一模一样
int lca(int a,int b){
if(dep[a]<dep[b])swap(a,b);
int d=dep[a]-dep[b];
for(int i=0;d;i++,d>>=1)
if(d&1)a=p[a][i];
if(a==b)return a;
for(int i=18;i>=0;i--)
if(p[a][i]!=p[b][i])
a=p[a][i],b=p[b][i];
return p[a][0];
}
//计算
void calc(int u,int v,int w){
int d=dep[u]-dep[v];//深度差,判断路径
int m1=0,m2=0;
for(int i=0;d;i++,d>>=1){
if(d&1){//如果可以往上跳
m2=max(m2,Smax[u][i]);//细节,先求次大值
if(Max[u][i]>m1){//如果更新最大值,那么原来的最大值m1可能为新的次大值
m2=max(m2,m1);//判断次大值是否更新
m1=Max[u][i];//更新最大值
}
}
}
if(m1==w)ans=min(ans,w-m2);//如果最大值与边相等,用次大值更新
else ans=min(ans,w-m1);//否则用最大值
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
f[i]=i;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&E[i].from,&E[i].to,&E[i].val);
Krs();
dfs(1);
for(int i=1;i<=m;i++){
if(!E[i].isin){
int u=E[i].from,v=E[i].to,Lca;
Lca=lca(u,v);
calc(u,Lca,E[i].val);
calc(v,Lca,E[i].val);
}
}
printf("%lld\n",tot+ans);
}
Tarjan
#include <bits/stdcpp.h>
typedef long long ll;
const int maxn=2000+10;
const int maxm=100000+10;
const int INF = 0x3f3f3f3f;
using namespace std;
vector<int>g[1010];
int n,m;
int scc_cnt,in[maxn],scc_be[maxn],dfn[maxn],low[maxn],cnt,S[maxn],top,isin[maxn];
void tarjan(int u){
dfn[u]=low[u]=++cnt;
S[++top]=u;
isin[u]=1; //判断u是否在栈中
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(isin[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
scc_cnt++;
while(1){
int x=S[top--];
scc_be[x]=scc_cnt;
isin[x]=0;
if(x==u) break;
}
}
}
void find_scc(){
memset(dfn,0,sizeof(dfn));
memset(in,0,sizeof(in));
memset(low,0,sizeof(low));
top=-1;
scc_cnt=0;
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
}
int main(){
while(~scanf("%d%d",&n,&m)){
int w[1010];
cnt = 0;
for(int i=1;i<=n;i++){
scanf("%d",&w[i]);
g[i].clear();
}
int u,v;
for(int i=0;i<m;i++){
scanf("%d%d",&u,&v);
g[u].push_back(v);
}
find_scc();
for(int i=1;i<=n;i++){
for(int j=0;j<g[i].size();j++){
int v=g[i][j];
if(scc_be[v]!=scc_be[i])
in[scc_be[v]]=1;
}
}
int res=0,num=0;
for(int i=1;i<=scc_cnt;i++){
if(!in[i]){
num++;
int mi=INF;
for(int j=1;j<=n;j++){
if(scc_be[j]==i)
mi=min(mi,w[j]);
}
res+=mi;
}
}
cout << endl;
printf("%d %d\n",num,res);
}
}