首先训练计划中的分类就错了,结果把我带偏了。这是一个求无向图连通分量的题目,然后看了几篇博客,才有点懂了。首先,要了解两个概念,欧拉图:是指含有欧拉回路的图,欧拉回路,是指经过每一条边一次且仅一次的的回路,如果是开路的话叫做欧拉路,含有欧拉路的图叫半欧拉图。所以题意很明确,只要构建图,添加边使得图为欧拉图即可,因为这样再减少任意一条边,这个图也还是半欧拉图,也是联通的。
又根据相关定理:
1.无向连通图G是欧拉图,当且仅当G不含奇数度结点(G的所有结点度数为偶数);
2.无向连通图G含有欧拉通路,当且仅当G有零个或两个奇数度的结点;
3.有向连通图D是欧拉图,当且仅当该图为连通图且D中每个结点的入度=出度
4.有向连通图D含有欧拉通路,当且仅当该图为连通图且D中除两个结点外,其余每个结点的入度=出度,且此两点满足deg-(u)-deg+(v)=±1。(起始点s的入度=出度-1,结束点t的出度=入度-1 或两个点的入度=出度)
5.一个非平凡连通图是欧拉图当且仅当它的每条边属于奇数个环。
6.如果图G是欧拉图且 H = G - uv,则H有奇数个u,v-迹仅在最后访问v;同时,在这一序列的u,v-迹中,不是路径的迹的条数是偶数。
可知只要保证图中的每一个点度数大于等于2即可了,
所以一共有三种做法:
1.利用优先队列,模拟加边过程
2.直接计算需要增加的度数,一条边增加两个度数
3.是由第二种方法得出的一种适合求所有图补成完美子图 边的条数的一种方法
注意:因为存在要补度数为奇数的情况,这时候要把奇数变为偶数再加一,而第一种方法模拟加边的过程中也存在最后剩余一个节点无法进行加边的情况,这样最后也需要加1,而第三种情况正是为了解决这种问题而产生的。
推荐使用第二种方法
参考文章 http://blog.csdn.net/change_ac/article/details/47657453
http://blog.csdn.net/johsnows/article/details/51736768
https://wenku.baidu.com/view/8afc272c580216fc700afd91.html
完美网络
Time Limit: 1000MS Memory limit: 65536K
题目描述
完美网络是连通网络的基础上要求去掉网络上任意一条线路,网络仍然是连通网络。求一个连通网络要至少增加多少条边可以成为完美网络。
输入
第一行输入一个数T代表测试数据个数(T<=20)。每个测试数据第一行2个数n,m 分别代表网络基站数和基站间线路数。基站的序号为从1到n。接下来m行两个数代表x,y 代表基站x,y间有一条线路。
(0
输出
对于每个样例输出最少增加多少线路可以成为完美网络。每行输出一个结果。
示例输入
2
3 1
1 2
3 2
1 2
2 3
示例输出
2
1
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
int book[1000009];
int main()
{
int t;
cin>>t;
int d[10000];
while(t--)
{
memset(d,0,sizeof(d));
int n,m;
cin>>n>>m;
int x,y;
for(int i=0;i<m;i++)
{
cin>>x>>y;
d[x]++;
d[y]++;
}
sort(d+1,d+1+n);
priority_queue<int,vector<int>,greater<int> >o;
for(int i=1;i<=n;i++)
{
if(d[i]<2)
o.push(d[i]);
else
break;
}
int sum = 0;
while(o.size()>=2)
{
x = o.top();
o.pop();
y = o.top();
o.pop();
x++;
y++;
sum++;
if(x<2)
o.push(x);
if(y<2)
o.push(y);
}
if(!o.empty())
sum++;
cout<<sum<<endl;
}
return 0;
}
方法2:
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n, m;
int sum[11234];
scanf("%d%d", &n, &m);
int i;
for(i=1; i<=n; i++)sum[i]=0;
for(i=1; i<=m; i++)
{
int x, y;
scanf("%d%d", &x, &y);
sum[x]++;sum[y]++;
}
int ans2=0;
for(i=1; i<=n; i++)
{
if(sum[i]<2)ans2+=2-sum[i];
}
if(ans2%2)
{
ans2++;
}
printf("%d\n", ans2/2);
}
return 0;
}
方法3:
方法3: sum1将所有度不为2的点连成环,但是这会使度为1的点之间出现多余边,sum2/2可得多余边的数量。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int degree[10010];
int main()
{
int t;
int n,m;
int i,x,y;
scanf("%d",&t);
while(t--)
{
memset(degree,0,sizeof(degree));
scanf("%d%d",&n,&m);
for(i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
degree[x]++;
degree[y]++;
}
int sum1=0,sum2=0;
for(i=1;i<=n;i++)
{
if(degree[i]<=1)
sum1++;
if(degree[i]==1)
sum2++;
}
sum=sum1-sum2/2;
if (sum2==0) sum++;
printf("%d\n",sum);
}
return 0;
}