欧拉图是指通过图(无向图或有向图)中所有边且每边仅通过一次的通路,相应的回路称为欧拉回路 。
//无向图欧拉通路、回路,每个边的度只能为1或2,度为1的点只能为0或2
#include
using namespace std;
int const maxn = 100;
vector<int>path;
int n,m,mmp[maxn][maxn],cnt[maxn];
string str;
void dfs(int o)
{
for(int i=0; i<n; i++)
{
if(mmp[o][i])
{
mmp[o][i] = mmp[i][o] = 0;
dfs(i);
}
}
path.push_back(o);
}
int main(){
cin>>n>>m;
int a,b;
for(int i=0; i<m; ++i){
cin>>a>>b;
mmp[a][b]=mmp[b][a]=1;
cnt[a]++;cnt[b]++;
}
int flag=0,x,y,Count=0;
for(int i=0; i<n; ++i)
if(cnt[i]&1){//度为奇数
Count++;//统计
if(Count==1)
x=i;//标记起点
else
y=i;//标记终点
}
else if(cnt[i]>0&&!flag)//若为欧拉回路,任意起点
flag=i;
if(Count==1||Count>2)
cout<<"非欧拉图";
else{
if(Count==0)
dfs(flag);
else
x<y?dfs(x):dfs(y);//模板例题要求从最小点输出
reverse(path.begin(),path.end());
for(int i=0; i<path.size(); ++i)
cout<<wdnmd[path[i]];
}
return 0;
}
洛谷P1341 无序字母对
在一个有向图中,对所有的节点进行排序,没有一个节点指向它前面的节点。
#include
using namespace std;
vector<int>edge[30];//邻接表
int in[30];//记录入度
set<int>mmp;//标记
int main()
{
int s1,s2;
while(cin>>s1>>s2)
{
mmp.insert(s1);
mmp.insert(s2);//标记图中记录的点
in[s2]++;//记录入度
edge[s1].push_back(s2);//记录邻接表
}
priority_queue<int,vector<int>,greater<int> >ans;//大部分题目要求将拓扑序列按照字典序最小的情况输出,故需要用到优先队列来处理
for(int i=0;i<30;i++)//入度为0的点入队
if(in[i]==0&&mmp.count(i)!=0)
ans.push(i);
queue<int>Count;//记录出队顺序以及数量
while(!ans.empty())
{
int flag=ans.top();
ans.pop();//入度为0的点出队
Count.push(flag);//按顺序入队
for(int i=0;i<edge[flag].size();i++)//将该点所发出的边去掉
{
in[edge[flag][i]]--;
if(in[edge[flag][i]]==0&&mmp.count(edge[flag][i])!=0)//如果有点的入度更新为0,则入优先队列
ans.push(edge[flag][i]);
}
}
if(Count.size()==mmp.size())//判断是否有环
while(!Count.empty())
{
cout<<Count.front();
Count.pop();
}
else cout<<"No Answer!";//有环的输出
return 0;
}
2153: D.ly的排队问题
从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径。
#include
using namespace std;
int const inf = 1e6+5;
int const maxn = 1e3+5;
int edge[maxn][maxn];
int path[100005],d[100005];
bool s[100005];
int v0=1;
int n,m;
void init()
{
for(int i=1; i<=n; ++i)
{
s[i]=false;
d[i]=edge[v0][i];
if(d[i]<inf)
path[i]=v0;
else
path[i]=-1;
}
s[v0]=true;
d[v0]=0;
}
void Dijkstra()
{
int circle=n-1;
while(circle--)
{
int Min=inf,v;
for(int i=1; i<=n; ++i)
{
if(!s[i]&&d[i]<Min)
{
v=i;
Min=d[i];
}
}
s[v]=true;
for(int i=1; i<=n; ++i)
{
if(!s[i]&&(d[v]+edge[v][i]<d[i]))
{
d[i]=d[v]+edge[v][i];
path[i]=v;
}
}
}
}
void output()
{
for(int i=1; i<=n; ++i)
if(i!=v0)
cout<<i<<" "<<d[i]<<endl;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;++i)
d[i]=inf;
for(int i=0;i<maxn;++i)
for(int u=0;u<maxn;++u)
edge[i][u]=inf;
while(m--)
{
int a,b,w;
cin>>a>>b>>w;
edge[a][b]=w;
edge[b][a]=w;
}
init();
Dijkstra();
output();
return 0;
}
#include
using namespace std;
const int maxn=1000;
const int maxv=1000;
const int inf=0x3f3f3f3f;
struct mmp
{
int to,cost;
};
int V;
vector<mmp>edge[maxn];//邻接表
int d[maxv];
void Dijstra(int start)
{
//通过指定greater>参数,堆按照first从小到大顺序取出值
priority_queue< pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>> > q;
fill(d,d+V,inf);//填充,第一个变量为首,第二个为尾,第三个为填充的值
d[start]=0;//初始化为0
q.push(pair<int,int>(0,start));//first是最短距离,second是顶点编号
while(!q.empty())
{
pair<int,int> temp=q.top();
q.pop();
int v=temp.second;//v表示顶点编号
if(d[v]<temp.first)
continue;
for(int i=0; i<edge[v].size(); i++)
{
mmp e=edge[v][i];
if(d[e.to]>d[v]+e.cost)
{
d[e.to]=d[v]+e.cost;
q.push(pair<int,int>(d[e.to],e.to));
}
}
}
}
#include
using namespace std;
int prev[maxn];
int d[maxn];
int n;
int edge[maxn][maxn];
bool used[maxn]
void Dijkstra(int s)
{
fill(d,d+v,inf);
fill(used,used+n,false);
fill(prev,prev+n,-1);
d[s]=0;
while(true)
{
int v=-1;
for(int i=0; i<n; i++)
if(!used[i]&& (v==-1 || d[i]<d[v]) )
v=i;
if(v==-1)
break;
used[v]=true;
for(int u=0; u<n; u++)
{
if(d[u]>d[v]+edge[v][u])
d[u]=d[v]+edge[v][u];
prev[u]=v;
}
}
}
vector <int>get_path(int t)
{
vector<int>path;
for(; t!=-1; t=prev[t])
path.push_back(t);
reverse(path.begin(),path.end());
return path;
}
HDU Today(hdu2112)
#include
using namespace std;
int const inf = 0x3f3f3f3f;
int const maxn = 1e3+5;
int path[maxn][maxn];
int d[maxn][maxn];
int edge[maxn][maxn];
int m,n;
void init()
{
for(int i=0; i<n; ++i)
for(int u=0; u<n; ++u)
{
d[i][u]=edge[i][u];
if(d[i][u]<inf && i!=u)
path[i][u]=i;
else
path[i][u]=-1;
}
}
void Floyd()
{
for(int i=0; i<n; ++i)
for(int u=0; u<n; ++u)
for(int o=0; o<n; ++o)
if(d[u][i]+d[i][o]<d[u][o])
{
d[u][o]=d[u][i]+d[i][o];
path[u][o]=path[i][o];
}
}
void output()
{
for(int i=0; i<n; ++i)
{
for(int u=0; u<n; ++u)
cout<<d[i][u]<<" ";
cout<<endl;
}
}
int main()
{
for(int i=0; i<maxn; ++i)
for(int u=0; u<maxn; ++u)
edge[i][u]=inf;
cin>>n>>m;
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
if(edge[a][b]>c)
edge[a][b]=edge[b][a]=c;
}
init();
Floyd();
output();
return 0;
}
HDU Shortest Path(hdu3631)
#include
using namespace std;
const int inf = 0x3fffffff;
const int maxn = 100;
int edge[maxn][maxn];
int d[maxn];
bool s[maxn];
int num[maxn];//记录每个结点入队的次数
int n,m;
bool spfa(int v0)
{
queue<int> q;
memset(s,false,sizeof(s));
memset(num,0,sizeof(num));
for(int i=0; i<n; i++)
d[i] = inf;
d[v0] = 0;
q.push(v0);
s[v0] = true;
num[v0]++;
while(!q.empty())
{
int p = q.front();
q.pop();
for(int i=0; i<n; i++)
{
if(d[p]+edge[p][i]<d[i])
{
d[i] = d[p]+edge[p][i];
if(!s[i])
{
q.push(i);
num[i]++;
if(num[i]>n)//存在负环
return false;
s[i]=true;
}
}
}
s[p] = false;
}
return true;
}
int main()
{
int a,b,c;
cin>>n>>m;
for(int i=0;i<maxn;++i)
for(int u=0;u<maxn;++u)
edge[i][u]=inf;
for(int i=0; i<m; ++i)
{
cin>>a>>b>>c;
edge[a][b]=edge[b][a]=c;
}
if(spfa(0))
for(int i=0; i<n; ++i)
cout<<d[i]<<endl;
else
cout<<"存在负环"<<endl;
return 0;
}
G=(V,E)为连通无向图,V为结点的集合,E为结点可能连接的边,对每条边(u ,v)都赋予权重w(u ,v)。找到一个无环子集T, 既能将所有结点连接起来,又具有最小权重。T是由G生成的树,这种问题叫做最小生成树问题。
将V的每个结点视为单独节点,定义一个根节点,从根节点开始将E中的边按权重从小到大依次处理。
首先判断边的两个结点是否存在孤立点,若存在,则合并两节点(合成一颗树),并更新根节点;若不存在,则不予理会。
下图为算法过程:
#include
using namespace std;
typedef long long ll;
int n, m;
ll ans;
struct Edge
{
int start, to;
int val;
} edge[500005];
int pre[100005];
int find(int x)
{
if(pre[x] == x)
{
return x;
}
else
{
pre[x]=find(pre[x]);
return pre[x];
}
}
bool cmp(Edge a, Edge b)
{
return a.val < b.val;
}
int kruskal()
{
for(int i = 1; i <= m; i++)//遍历边
{
int total=1;
int u=find(edge[i].start);
int v=find(edge[i].to);
if(u == v)
continue;
ans += edge[i].val;
pre[u] = v;
total++;
if(total == n)
break;
}
return ans;
}
int main()
{
cin >> n >> m;
int root,res=0;
cin >> root;
for(int i = 1; i <= n; i++)
pre[i] = i;
for(int i = 1; i <= m; i++)
cin >> edge[i].start >> edge[i].to >> edge[i].val;
sort(edge + 1, edge + m + 1, cmp);
cout << kruskal() << endl;
return 0;
}
给定连通图G和任意根节点r,最小生成树从结点r开始,一直扩大直到覆盖V中所有结点为止,即不断寻找轻量级边以实现最小权重和。
下图为算法过程:
#include
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 100;
int n, m;
int edge[maxn][maxn];//地图
int d[maxn];//存储距离
int s[maxn];//标记
void init(int v0)
{
for(int i=1; i<=n; ++i)
{
s[i]=false;
d[i]=edge[v0][i];
}
s[v0]=true;
d[v0]=0;
}
int prim(int v0) //传进起始点
{
int sum = 0; //权值总和
for(int i = 1; i <= n - 1; i ++)
{
// n - 1次迭代
int minn = inf;
int v;
for(int u = 1; u <= n; u ++) //找到距离最小的点
if(!s[u] && d[u] < minn)
{
minn = d[u];
v = u;
}
s[v] = 1;
sum += minn; //加上边权值
for(int u = 1; u <= n; u ++) //松弛操作
{
if(!s[u] && d[u] > edge[v][u])
d[u] = edge[v][u];
}
}
return sum;
}
int main()
{
for(int i=0; i<maxn; ++i)
d[i]=inf;
for(int i=0; i<maxn; ++i)
for(int u=0; u<maxn; ++u)
edge[i][u]=inf;
cin >> n >> m;
int v0,res=0;
cin >> v0;
int a,b,c;
for(int i = 1; i <= m; i++)
{
cin >> a >> b >> c;
edge[a][b]=c;
}
init(v0);
cout << prim(v0) << endl;
return 0;
}
CCF-CSP201812-4数据中心(最小生成树)
推荐博客
#include
using namespace std;
const int maxn=1000;
const int maxm=100000;
const int inf=0x3f3f3f3f;
struct Edge
{
int to,c,next;
} edge[maxm];
int p[maxn];//记录每个点的下标
int cnm=0;//总点数
void addedge(int a,int b,int c)
{
edge[cnm].to=b;
edge[cnm].next=p[a];
edge[cnm].c=c;
p[a]=cnm++;
}
int S,T; //源点和汇点
int d[maxn]; //d表示当前点的层数(level)
bool bfs()
{
memset(d,-1,sizeof(d));
queue<int>q;
q.push(S);
d[S]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=p[u]; i!=-1; i=edge[i].next)
{
int v=edge[i].to;
if(edge[i].c>0 && d[v]==-1)
{
q.push(v);
d[v]=d[u]+1;
}
}
}
return (d[T]!=-1);
}
int dfs(int u,int f)
{
if(u==T)
return f;
int res=0;
for(int i=p[u]; i!=-1; i=edge[i].next)
{
int v=edge[i].to;
if(edge[i].c>0 && d[u]+1 == d[v])
{
int tmp=dfs(v,min(f,edge[i].c)); //递归计算顶点v,用c(u,v)更新当前流量上限
f-=tmp;
edge[i].c-=tmp;
res+=tmp;
edge[i^1].c+=tmp; //修改反向弧流量
if(f==0) //流量达到上限,不必继续搜索
break;
}
}
if(res==0) //当前没有经过顶点u的流,不必再搜索顶点u
d[u]=-1;
return res;
}
int maxflow() //计算最大流
{
int res=0;
while(bfs())
{
res+=dfs(S,inf); //初始流量上限为inf
}
return res;
}
int main()
{
memset(p,-1,sizeof(p));
cnm=0;
int m,n;
cin>>n>>m;//n个点m条边
for(int i=0; i<m; i++)
{
int a,b,c;
cin>>a>>b>>c;
addedge(a,b,c);
addedge(b,a,0);
}
/*
for(int i=1;i<=n;i++) //输出图,用于检测图的读入是否正确
for(int u=p[i];u!=-1;u=edge[u].next)
{
cout<"<
S=1;
T=n;
cout<<maxflow()<<endl;
return 0;
}