题目意思我就借别人博客里的啦,素质借用,附上原文地址http://blog.sina.com.cn/s/blog_68d7b63901013guv.html
Arbiter
Input
第一行输入有一个整数 T,表示测试用例的数目。
每个用例的第一行有两个整数 N 和 M,表示星球的数目和航道的数目。随后的 M 行表示航道,每个 (u, v) 代表在星球 u 和星球 v 之间有一条双向的航道(u 不等于 v)。
Output
Constraints
就是问你至少要去掉几条边,才能使一个人从任何一个星球出发,然后转个圈回到自己的星球经过的边的数目是偶数。
二分图就有这个性质,二分图中若存在环,环的边数一定是偶数。数据不是很大,可以枚举。一共n个顶点,用2 ^(n-1)次方来表示状态。
例如n = 5,00001(二进制)表示顶点0属于集合1,顶点1,2,3,4,属于集合2.如果同一个集合里的两个顶点有边相连,这条边就是要去掉的(二分图的性质)。
所以我们枚举每种状态,然后找出最少的不合法的边的数码,即同一个集合里的顶点相连的边。这就是我们要的答案了。
一开始我是找出连接两个集合的边,即边的一个顶点在集合1,另一个顶点在集合2的边,然后用边的总数m减去这样的边,这样就可以少一半的操作,结果果断wa!
非要我改成第一种找法,不知道是数据问题还是方法问题,求过路的看官给个解答。
AC代码:
#include<stdio.h>
int main()
{
int T,n,m;
scanf("%d",&T);
while(T--)
{
int map[20][20] = {0},a,b;
scanf("%d%d",&n,&m);
for(int i = 0;i < m;++i)
{
scanf("%d%d",&a,&b);
map[a][b]++;
map[b][a]++;
}
int min = 0x7fffffff;
for(i = 1;i < (1 << n) - 1;++i)//枚举每个状态
{
int sum = 0;
for(int j = 0;j < n;++j)//对每个状态的每一位进行操作
{
if((1 << j) & i)//如果i的这一位为1,表示属于集合2
{
for(int k = j + 1;k < n;++k) //那么就找出同样是集合2的点,然后sum加上他们之间不合法的边
if((1 << k) & i)
sum += map[j][k];
}
else
{
for(int k = j + 1;k < n;++k)//同理,找出同是集合1的点。
if(!((1 << k) & i))
sum += map[j][k];
}
}
if(sum < min) min = sum;这就是最少要去掉的边
}
printf("%d\n",min == 0x7fffffff?0:min);
}
return 0;
}
wa代码
#include<stdio.h>
int main()
{
int T,n,m;
scanf("%d",&T);
while(T--)
{
int map[20][20] = {0},a,b;
scanf("%d%d",&n,&m);
for(int i = 0;i < m;++i)
{
scanf("%d%d",&a,&b);
map[a][b]++;
map[b][a]++;
}
int min = 0x7fffffff;
for(i = 1;i < (1 << n) - 1;++i)
{
int sum = 0;
for(int j = 0;j < n;++j)
{
if((1 << j) & i)
{
for(int k = j + 1;k < n;++k)
{
if(!((1 << k) & i))
sum += map[j][k];
}
}
}
if(m - sum < min)
min = m - sum;
}
printf("%d\n",min == 0x7fffffff?0:min);
}
return 0;
}