floyed,二分图,prime
1.prim算法
(1)朴素版
介绍:
在带权连通图中,V是所有点的集合,U是已经生成最小的树的顶点的集合。
每次都要找到“除了U里面的顶点,还剩余的顶点里面距离U集合最近的一个点,这个点加入U"。
这里抛砖引玉一下,具体看这里,复习非常好:prim算法介绍
题目:prim
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=510;
int map[N][N];
int dist[N];
int st[N];
int n,m;
int sum;
bool prim()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
{
if(st[j]==0&&(t==-1||dist[t]>dist[j]))
t=j;
}
st[t]=1;
sum+=dist[t];
if(dist[t]==0x3f3f3f3f)
{
cout<<"impossible";
return false;
}
for(int j=1;j<=n;j++)
{
if(st[j]==0&&dist[j]>map[t][j])
dist[j]=map[t][j];
}
}
return true;
}
int main()
{
cin>>n>>m;
memset(map,0x3f,sizeof map);
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
map[a][b]=min(map[a][b],c);
map[b][a]=map[a][b];
}
if(prim())
cout<<sum;
return 0;
}
(2)堆优化版
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef pair<int,int>PII;
const int N=1e6+10;
int head[N],e[N],ne[N],w[N],idx;
int st[N];
int n,m;
int sum=0;
void add(int a,int b,int c)
{
e[idx]=b,ne[idx]=head[a],w[idx]=c,head[a]=idx++;
}
void prim()
{
priority_queue<PII,vector<PII>,greater<PII>>q; //优先队列
q.push({0,1});
int cnt=0;
while(q.size())
{
PII k=q.top(); //堆顶的就是距离生成树集合最近的点
q.pop();
int distance=k.first,ver=k.second;
if(st[ver]==1)continue; //这句话看起来不需要,其实需要,因为可能堆里面有多个一样的点,但是只有第一个被标记为1且出队。实际上后面这个点都要被标记为1,但是没有标记。理解一下,模拟一下。
st[ver]=1;
sum+=distance;
cnt++;
for(int i=head[ver];i!=-1;i=ne[i])
{
int j=e[i];
if(st[j]==0)
{
q.push({w[i],j});
}
}
}
if(cnt!=n)
cout<<"impossible";
else
cout<<sum;
}
int main()
{
cin>>n>>m;
memset(head,-1,sizeof head);
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
prim();
return 0;
}
2.floyed算法
题目:添加链接描述
思路:动态规划,从a到b的最短距离等于从a到k和k到b的距离之和。
有点向区间dp。反正这样记忆就好了,第一层for循环必须是k
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=210,INF=0x3f3f3f3f;
int dist[N][N];
int n,m,Q;
void floyd()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
}
int main()
{
cin>>n>>m>>Q;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i==j)
dist[i][j]=0;
else
dist[i][j]=INF;
}
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
dist[a][b]=min(dist[a][b],c);
}
floyd();
while(Q--)
{
int a,b;
cin>>a>>b;
if(dist[a][b]>INF/2)
cout<<"impossible"<<endl;
else cout<<dist[a][b]<<endl;
}
return 0;
}
3.kruskal算法
思想:排序+并查集
对于枚举的边,如果当前边的两个点都在生成树集合内,则这条边不要,否则就要.
题目:添加链接描述
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200010;
int p[N]; //并查集
struct node
{
int a,b,w;
bool operator <(const node &t)
{
return this->w<t.w;
}
}edge[N*2];
int res=0;
int cnt,n,m;
int find(int x)
{
if(x!=p[x])
p[x]=find(p[x]);
return p[x];
}
void kruskal()
{
for(int i=1;i<=m;i++)
{
int pa=find(edge[i].a);
int pb=find(edge[i].b);
if(pa!=pb)
{
res+=edge[i].w;
p[pa]=pb;
cnt++;
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)p[i]=i;
for(int i=1;i<=m;i++)
{
int a,b,c;
cin>>a>>b>>c;
edge[i].a=a,edge[i].b=b,edge[i].w=c;
}
sort(edge+1,edge+m+1);
kruskal();
if(cnt<n-1)
cout<<"impossible";
else
cout<<res;
return 0;
}
4.染色法判定二分图
什么是二分图:(引用)
有两顶点集且图中每条边的的两个顶点分别位于两个顶点集中,每个顶点集中没有边直接相连接!
说人话的定义:图中点通过移动能分成左右两部分,左侧的点只和右侧的点相连,右侧的点只和左侧的点相连。
题目:染色法
#include<iostream>
#include<cstring>
using namespace std;
const int N=200010;
int head[N],e[N],ne[N],idx;
int st[N];
int n,m;
void add(int a,int b)
{
e[idx]=b,ne[idx]=head[a],head[a]=idx++;
}
bool dfs(int u,int col)
{
st[u]=col;
for(int i=head[u];i!=-1;i=ne[i])
{
int j=e[i];
if(st[j]==0)
{
if(!dfs(j,3-col))
return false;
}
else
{
if(st[j]==col)
return false;
}
}
return true;
}
int main()
{
cin>>n>>m;
memset(head,-1,sizeof head);
for(int i=0;i<m;i++)
{
int a,b;
cin>>a>>b;
add(a,b),add(b,a);
}
for(int i=1;i<=n;i++)
{
if(st[i]==0)
{
if(!dfs(i,1))
{
cout<<"No";
return 0;
}
}
}
cout<<"Yes";
return 0;
}
5.匈牙利算法
题目:
二分图的最大匹配
#include<iostream>
#include<cstring>
using namespace std;
const int N = 510 , M = 100010;
int n1,n2,m;
int h[N],ne[M],e[M],idx;
bool st[N];
int match[N];
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;//那x就预定这个女孩了
//如果女孩j没有男朋友,或者她原来的男朋友能够预定其它喜欢的女孩。配对成功
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 res = 0;
for(int i = 1; i <= n1 ;i ++)
{
//因为每次模拟匹配的预定情况都是不一样的所以每轮模拟都要初始化
memset(st,false,sizeof st);
if(find(i))
res++;
}
cout<<res<<endl;
}