题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5631
解题思路:
这题要求删除边,使得无向图继续连通。
由于n个节点只有n+1条边,所以要么删除一条边,要么删除两条边。
数据量比较小,可以枚举要删除的边即可。
删除一条边很简单,关键是怎么删除两条边。这里提供一个用图论的方法解决。
假设我们先枚举删除的两条边中的一条,那么要在剩下的图当中再找一条边。到底要删哪一条呢?反正不能是割边对吧,原因你懂的。
那么这个问题转化为求割边数量了。。。剩下的边减去割边就是可以再删掉的一条边。。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
using namespace std;
const int maxn = 105;
struct Edge
{
int u,v;
}edge[maxn];
int n,ans,sum,map[maxn][maxn];
int dfsn[maxn],Index,low[maxn];
bool cut[maxn][maxn];
void Tarjan(int u,int fa)
{
dfsn[u] = low[u] = ++Index;
for(int i = 1; i <= n; i++)
{
if(map[u][i] == 0 || i == fa) continue;
if(dfsn[i] == 0)
{
Tarjan(i,u);
low[u] = min(low[u],low[i]);
if(low[i] > dfsn[u] && map[u][i] == 1)
cut[u][i] = cut[i][u] = true;
}
else low[u] = min(low[u],dfsn[i]);
}
}
void solve()
{
Index = 0;
memset(dfsn,0,sizeof(dfsn));
memset(cut,false,sizeof(cut));
Tarjan(1,0);
for(int i = 1; i <= n; i++)
if(dfsn[i] == 0) //去掉一条边后不连通
{
sum--;
return;
}
int cnt = 0; //割边数量
for(int i = 1; i <= n; i++)
for(int j = i + 1; j <= n; j++)
if(cut[i][j] == true)
cnt++;
ans += n - cnt;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
memset(map,0,sizeof(map));
ans = 0;
sum = n + 1; //sum表示只去掉一条边的条数
for(int i = 1; i <= n + 1; i++)
{
scanf("%d%d",&edge[i].u,&edge[i].v);
map[edge[i].u][edge[i].v]++;
map[edge[i].v][edge[i].u]++;
}
for(int i = 1; i <= n + 1; i++) //删除第i条边
{
map[edge[i].u][edge[i].v]--;
map[edge[i].v][edge[i].u]--;
solve();
map[edge[i].u][edge[i].v]++; //恢复第i条边
map[edge[i].v][edge[i].u]++;
}
printf("%d\n",ans / 2 + sum);
}
return 0;
}