算法思路:
(1)初始化所有点到集合的距离为正无穷;
(2)for n次 迭代n次为了把所有点加到树中
每次找出:不在集合内 && (第一次走||不在集合内的最小点)
用最小点t更新所有点到集合的最小距离
最后把最小点t加入到集合内(标记为TRUE)
代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 510
#define INF 0x3f3f3f3f
using namespace std;
int n,m;
int g[N][N];//存图
int dist[N];//存点到集合的距离
bool st[N];
int prim()
{
int res=0;//最小生成树的边权和
memset(dist,0x3f,sizeof dist);
for(int i=0;i<n;i++)//迭代n次,选完所有点
{
int t=-1;
for(int j=1;j<=n;j++)
{
if(!st[j] &&(t==-1 || dist[t]>dist[j]))
//不在集合内 && (第一次||不在集合内的最小点)
t=j;
}
if(i && dist[t]==INF)
//不是第一次而且所选的最小距离都是INF,不存在最小生成树
return INF;
if(i)//不是第一次选的无穷点,就累加路径
res+=dist[t];
for(int j=1;j<=n;j++)//用t更新所有点到集合的最小距离
dist[j]=min(dist[j],g[t][j]);
st[t]=true;//加到集合中
}
return res;
}
int main()
{
cin>>n>>m;
memset(g,0x3f,sizeof g);
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
g[a][b]=g[b][a]=min(g[a][b],c);//图是无向边
//防止多条边,求出最短边
}
int t=prim();
if(t == INF) puts("impossible");
else cout << t << endl;
return 0;
}
Kruskal算法求最小生成树
算法思路:
(1)把所有边按照权值从小到大排序
(2)从小到大遍历边,如果边的两个点没有连接,就把他们用并查集连起来
(3)如果a的祖先和b的祖先一样,说明这两个点已经连过了,再连会产生回环。
#include<iostream>
#include<algorithm>
#define N 200010
using namespace std;
int n,m;
int p[N];
struct edge{
int a,b,w;
bool operator < (const edge &W)const
{//当结构体用<时规则按照w来比
return w<W.w;
}
}edges[N];
int find(int x)
{
if(p[x]!=x)
p[x]=find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b,w;
cin>>a>>b>>w;
edges[i]={a,b,w};
}
sort(edges,edges+m);
for(int i=1;i<=n;i++)
p[i]=i;//并查集自己是自己的父亲
int res=0,cnt=0;
for(int i=0;i<m;i++)//遍历边
{
int a=edges[i].a,b=edges[i].b,w=edges[i].w;
a=find(a),b=find(b);
if(a!=b)//如果没有联通的话,说明不在一个集合内
{
p[b]=a;//ab相连
res+=w;//记录最小生成树的边权和
cnt++; //边数量++
}
}
if(cnt<n-1)//边小于结点数-1就不能生成
cout<<"impossible"<<endl;
else
cout<<res<<endl;
return 0;
}
二分图:(把所有点划分到两边,两集合内都有边)
(1)二分图当且仅当图中不含奇数环(边数为奇数)
染色法:
开始对任意一未染色的顶点染色。
判断其相邻的顶点中,若未染色则将其染上和相邻顶点不同的颜色。
若已经染色且颜色和相邻顶点的颜色相同则说明不是二分图,若颜色不同则继续判断。
bfs和dfs可以搞定!
DFS:
#include<stdio.h>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
#define M 200010
int e[M],ne[M],h[N],idx;
//int st[N];
int color[N];
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
bool dfs(int u,int c)
{
color[u]=c;//u点染c
for(int i=h[u];i!=-1;i=ne[i])
{
int b=e[i];//下一个点
if(!color[b])//没染过色就去染
{
if(!dfs(b,3-c))//染过之后出问题了就f
return false;
}
else if(color[b]&&color[b]!=3-c)//染过了但是不是另一种颜色
return false;
}
return true;
}
int main()
{
int n,m;
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--)
{
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);//无向图
}
bool flag=true;
for(int i=1;i<=n;i++)
{
if(!color[i])//未染色的话
{
if(!(dfs(i,1)))//递归和他相邻点
//如果递归该点的过程中有矛盾就no
{
cout<<"No"<<endl;
return 0;
}
}
}
cout<<"Yes"<<endl;
return 0;
}
BFS:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 100010
#define M 200010
#include<queue>
using namespace std;
typedef pair<int, int>PII;
int e[M],ne[M],h[N],idx;
int n,m;
int st[N];
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
bool bfs(int u)
{
queue<PII> q;
q.push({u,1});
st[u]=1;
while(!q.empty())
{
PII t;
t=q.front();
q.pop();
int ver=t.first;
int col=t.second;
for(int i=h[ver];i!=-1;i=ne[i]){
int j=e[i];
if(!st[j])
{
st[j]=3-col;
q.push({j,3-col});
}
else if(st[j]==col)
return false;
}
}
return true;
}
int main(){
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while(m --){
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
int flag = true;
for(int i = 1; i <= n; i ++) {
if (!st[i]){
if(!bfs(i)){
flag = false;
break;
}
}
}
if (flag) puts("Yes");
else puts("No");
return 0;
}
匈牙利算法:(最大匹配算法)
两个区间求最大匹配数
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 510
#define M 100010
using namespace std;
bool st[N];//模拟匹配的时候用
int h[N],ne[M],e[M],idx;
int n1,n2,m;
int match[N];//女孩i现在匹配的男友
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void init()
{
memset(h,-1,sizeof h);
}
int 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]||find(match[j]))
{//女方没有被匹配||他匹配的男友有其他选择
match[j]=x;
return true;
}
}
}
return false;
}
int main()
{
init();
cin>>n1>>n2>>m;
while(m--)
{
int a,b;
cin>>a>>b;
add(a,b);
}
int cnt=0;//最大匹配数
for(int i=1;i<=n1;i++)//左集合数量
{//看加入这个男的能不能扩大匹配数
memset(st,false,sizeof st);//st是用来当前模拟匹配的
if(find(i))
cnt++;
}
cout<<cnt<<endl;
return 0;
}