传送门:题目链接
题意:
问题描述
给出N 个球,和M个箱子,第i个球可以放进编号Ai箱子或者编号Bi箱子。你希望最后箱子内 包含奇数个球的箱子个数尽量少,问最少有多少个包含奇数个球的箱子。
输入格式
第一行两个整数N,M。
接下来N 行,每行2个整数,第i行两个整数依次表示Ai,Bi。
输出格式
一行一个整数表示答案。
数据范围
• 对于50%的数据,N ≤20。
• 对于100%的数据,1≤N,M ≤100000, 1≤Ai,Bi ≤M。
样例
样例输入
2 3
1 2
2 3
样例输出
0
做题过程
从“每个球可以放进两个箱子”这个条件。我就联想到图论,球看作边,箱子看成点。整个图就可以直接建出来。
建出来之后在图上尝试过几种方法:
1. 图上贪心,按照顺序每次尽量选择还是奇数个的箱子放。发现是错误的。反例...
2. Dfs建树然后贪心,..........................................。发现还是错误的,反例.....
最后提交的是暴力,得分50.
题解
建完图之后,每个连通块单独考虑。可以发现,一个联通块中如果出现两个奇数个球的箱子,则这两个箱子可以抵消掉。证明方法如下:
......
由此可以推出每个连通块最多会存在一个奇数个球的箱子(超过1个就可以任选两个抵消,直到没有为止)。
由于每个联通块的球的个数奇偶性是固定的,因此只需要看每个连通块的边数是奇数还是偶数就可以啦。
AC代码:
#include <bits/stdc++.h>
using namespace std ;
int N, M;
const int MAXN = 100010 ;
int fa[MAXN], g[MAXN] ;
int findfa(int x) {
return fa[x] == x ? x : fa[x] = findfa(fa[x]);
}
int main() {
scanf("%d%d", &N, &M) ;
for (int i = 1; i <= M; i ++)
fa[i] = i ;
for (int i = 1; i <= N; i ++) {
int u, v;
scanf("%d%d", &u, &v) ;
if (findfa(u) != findfa(v)) {
g[findfa(u)] ^= g[findfa(v)] ;
fa[findfa(v)] = findfa(u) ;
}
g[findfa(u)] ^= 1 ;
}
int ans = 0 ;
for (int i = 1; i <= M; i ++)
if (fa[i] == i)
ans += g[i];
cout << ans << endl ;
return 0 ;
}
反思总结
建完图之后把问题想太难了,以为是贪心之类的算法,没想到是数奇数偶数。
思维拓展
举例:如果我们想要偶数个球的箱子尽量少,还能做吗?
举例2:如果每个球可以放进三个箱子,还能做吗?
举例3:如果我们想要奇数个球的箱子尽量多,还能做嘛?