本代码!!!!纯粹的暴力!!!!复杂度……反正很高……不适用于大数据!!!!
样例1(欧拉图):
7
10
1 2 4
1 3 1
1 4 3
1 7 4
2 4 5
3 4 3
4 7 2
5 6 2
5 7 10
6 7 7
样例2(非欧拉图):
7
10
1 2 1
1 6 3
1 7 2
2 3 1
3 4 4
3 7 2
4 7 3
4 5 2
5 6 1
6 7 3
/*难点:
1、对于非简单图,如何去储存它边和点的信息(要记录的元素有:点、多条边权、邻接点、度数,路径)
3、在连接奇点使其边偶点时使新增的边权最小
3、用fleury算法判读应该选取的边(即当有其他边可选时,不选割边)
*/
/*
解决方案
1、刚开始是想用一个结构体和几个数组来存的,后来想了想几个数组一把梭算了md
2、刚开始看着网上有个优化好像挺不错的,后来发现,我还是暴力递归写得舒服………舒服的一批
即用Foloyd算法算出所有奇点之间的最短路,然后枚举选出奇点的完美匹配
3、每取一次变就判断取过该变所产生的图是否连通,可以直接用搜索解决;
*/
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
#define N 1000000000
int E,V,sum,sta; //边、点、路径总和、开始点
int C[1000][1000],A[1000][1000],G[1000][1000]; //记录连通边的边权,两点之间的最短长度,邻接矩阵
int P[1000][1000]={0}; //记录最短路路径
bool vis[1000];
int d[1000];
int sg[1000],ssg,cont[1000]; //额外开一个数组,用于储存奇点是哪个点,个数
//输入信息
void input(){
ssg=0,sum=0,sta=0;
//Foloyd算法的初始化过程
for(int i = 0 ;i < 1000;i++)
for(int j = 0;j < 1000;j++){
G[i][j]=0;
if(i == j) A[i][j] = 0,C[i][j] = 0;
else A[i][j] = N,C[i][j] = N;
}
printf("请输入顶点个数:\n");
scanf("%d",&V);
printf("请输入边的条数:\n");
scanf("%d",&E);
int n=E,t=0;
printf("请输入所有的邻接的两个点和他们的边:\n");
while(n--){
int a,b,l;
scanf("%d %d %d",&a,&b,&l); //相应的处理读到的信息
d[a]++; //a点度数加一
d[b]++; //b点度数加一
G[b][a]++; //邻接矩阵的初始化
G[a][b]++;
A[a][b] = l; //Foloyd算法的初始化过程
A[b][a] = l;
C[a][b] = l;
C[b][a] = l;
sum+=l; //权重
//printf("d[%d]=%d 'd[%d]=%d\n",a,d[a],b,d[b]);
}
printf("请输入你的起始点:");
scanf("%d",&sta);
for(int i=1;i<=V;i++)//记录奇点的个数
if(d[i]%2==1)
sg[++ssg]=i;
}
bool connect(int op,int v){ //用于判断是否是连通图
if(v==0) return true;
memset(vis,0,sizeof(vis));
queue<int>q;
q.push(op);
vis[op]=1;
int mm=1;
while(!q.empty()){
int node=q.front();
for(int i=1;i<=V;i++){
if(!vis[i]&&G[node][i]>0){
vis[i]=1;
mm++;
q.push(i);
}
}
q.pop();
}
//printf("连通点的个数:%d\n",mm);
if(mm==v) return true;
else return false;
}
//想了想还是决定用Foloyd算法,复杂度什么的,懒得考虑了。
void Foloyd(int k){
if(k>V) return;
for(int i = 1;i <= V;i++){
for(int j = 1; j <= V; j++){
if(A[i][j] > A[i][k] + A[k][j]){
P[i][j] = k;
A[i][j] = A[i][k] + A[k][j];
}
}
}
Foloyd(++k);
}
int adde(int cn){//直接不计较复杂度的暴力递归。将奇点进行匹配得一个最小的
int ans=N;
if(cn<2) return 0; //奇点个数小于2,无需匹配。
for(int i=1;i<=cn;i++){
if(sg[i]!=0){
for(int j=i+1;j<=cn;j++){
if(sg[j]!=0){
int tem1=sg[i],tem2=sg[j];
sg[i]=0;
sg[j]=0;
if(ans>adde(cn-2)+A[tem1][tem2]){
cont[i]=tem2; //第i个奇点匹配的奇点是第j个奇点
cont[j]=tem1; //第j个奇点匹配的奇点是第i个奇点
ans=adde(cn-2)+A[tem1][tem2];
}
sg[i]=tem1;
sg[j]=tem2;
}
}
}
}
return ans;
}
void addpath(int cn){
memset(vis,0,sizeof(vis));
for(int i=1;i<=cn;i++){
if(!vis[sg[i]]){
vis[sg[i]]=1;
vis[cont[i]]=1;
while(P[sg[i]][cont[i]]){
//printf("is ok?%d %d\n",sg[i],cont[i]);
int sss=cont[i];
d[cont[i]]++; //加边操作
cont[i]=P[sg[i]][cont[i]];
d[cont[i]]++;
G[sss][cont[i]]++;G[cont[i]][sss]++;
//printf("?????%d %d %d\n",sss,cont[i],sg[i]);
}
d[sg[i]]++;d[cont[i]]++; //加边操作
G[sg[i]][cont[i]]++;G[cont[i]][sg[i]]++;
}
}
}
int test(){ //计算当前图中点的个数
int ss=0;
for(int i=1;i<=V;i++)
if(d[i]>0)
ss++;
//printf("est:%d\n",ss);
return ss;
}
void Fleury(int sta){
int i,cpn=0;
int vi = sta; // v0e1v1…eivi已经选定
printf("最优路径之一:\n");
while(true)
{
// 找一条不是割边的边ei+1
for(i = 1; i <= V; i++)
{
if (G[vi][i] > 0)
{
// 假设选定(vi,i)这条边
G[vi][i]--;
G[i][vi]--;
d[i]--;d[vi]--;
if(!connect(i,test())) // 这里一定要传i,这是欲选择边的末端,它应该在连通图中
{
G[vi][i]++;
G[i][vi]++;
d[i]++;d[vi]++;
continue;
}
// 选定(vi,i)这条边,打印路径
if(cpn==0)
printf("%d->%d",vi,i);
else
printf("->%d",i);
cpn++;
vi = i;
break;
}
}
if (i > V)
{
printf("\n");
break; // 边找完了
}
}
}
int main(){
input();
if(!connect(sta,V)){
printf("输入的图不是连通图\n");
return 0;
}
Foloyd(1);
sum+=adde(ssg);
addpath(ssg);
printf("最优巡回的总权重是:%d\n",sum);
Fleury(sta);
return 0;
}
总结:其实想了想,不该看网上那胡诌的想法的,搞得自己想的太多了,太复杂了。看看暴力是多么的优雅(虽然过不了题,但是写的开心)…
emm……不过算法还是要多学多写的,还是要尽量去优化的,不能一味的暴力。