A - 过山车
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const int N = 505;
int line[N][N];
int girl[N], used[N];
int k, m, n;
bool found(int x) {
for (int i = 1; i <= n; i++) {
if (line[x][i] && !used[i]) {
used[i] = 1;
if (girl[i] == 0 || found(girl[i])) {
girl[i] = x;
return 1;
}
}
}
return 0;
}
int main() {
int x, y;
while (scanf("%d", &k) && k) {
scanf("%d %d", &m, &n);
memset(line, 0, sizeof(line));
memset(girl, 0, sizeof(girl));
for (int i = 0; i < k; i++) {
scanf("%d %d", &x, &y);
line[x][y] = 1;
}
int sum = 0;
for (int i = 1; i <= m; i++) {
memset(used, 0, sizeof(used));
if (found(i))
sum++;
}
printf("%d\n", sum);
}
return 0;
}
题解:匈牙利算法的板子题
B - 50 years, 50 colors
#include <bits/stdc++.h>
using namespace std;
int n, k;
int a[110][110];
int col[60];
int book[110];
int match[110];
int newcol;
int dfs(int u) {
for (int i = 1; i <= n; i++) {
if (book[i] == 0 && a[u][i] == newcol) {
book[i] = 1;
if (match[i] == 0 || dfs(match[i])) {
match[i] = u;
return 1;
}
}
}
return 0;
}
int hungarian(int u) {
newcol = u; //记录当前颜色
memset(match, 0, sizeof(match));
int ans = 0;
for (int i = 1; i <= n; i++) {
memset(book, 0, sizeof(book));
if (dfs(i))
ans++;
}
return ans;
}
int main() {
while (~scanf("%d %d", &n, &k) && n + k) {
memset(a, 0, sizeof(a));
memset(col, 0, sizeof(col));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%d", &a[i][j]);
col[a[i][j]]++; //记录每个颜色出现的次数
}
}
vector<int> v;
for (int i = 1; i <= 50; i++) {
if (col[i] > 0) {
if (hungarian(i) > k) {
v.push_back(i); //插入i
}
}
}
if (v.size() == 0)
printf("-1\n");
else {
printf("%d", v[0]);
for (int i = 1; i < v.size(); i++)
printf(" %d", v[i]);
printf("\n");
}
}
return 0;
}
题解:二分图的最小点覆盖数(二分图的最大匹配)
行为左边,列为右边,对每种颜色判断这种颜色的气球最少需要多少次才能完全被扎破,尽可能的选出小的行号或列号,然后判断是否正好把所有颜色的气球都扎破了题解链接
C - Escape
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <functional>
#include <iostream>
using namespace std;
int maps[100005][20]; //邻接矩阵
int vis[20];
int cy[20][100005]; //点集Y的匹配情况
int numy[20], limit[20]; // numy代表这个星球的匹配数量
int n, m;
int path(int u) //找增广路
{
int i, j;
for (i = 0; i < m; i++) {
if (maps[u][i] && !vis[i]) {
vis[i] = 1;
if (numy[i] < limit[i]) {
cy[i][numy[i]++] = u;
return 1;
} else {
for (j = 0; j < limit[i]; j++)
if (path(cy[i][j])) {
cy[i][j] = u;
return 1;
}
}
}
}
return 0;
}
int MulMatch() //二分图多重匹配
{
int ans = 0;
memset(cy, 0, sizeof(cy));
memset(numy, 0, sizeof(numy));
for (int i = 0; i < n; i++) {
memset(vis, 0, sizeof(vis));
if (!path(i))
return 0;
}
return 1;
}
int main() {
int i, j, a;
while (scanf("%d%d", &n, &m) != EOF) {
memset(maps, 0, sizeof(maps));
for (i = 0; i < n; i++)
for (j = 0; j < m; j++)
scanf("%d", &maps[i][j]);
for (i = 0; i < m; i++)
scanf("%d", &limit[i]);
if (MulMatch())
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
题解:还是二分图匹配,只是要多加一个判断是否超过允许的链接数量限制题解链接
D - Necklace
#include <stdlib.h>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <string>
#include <vector>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int N = 10;
int n, m, u, v, ans, pos[N], link[N];
bool s[N][N], vis[N];
vector<int> eg[N];
bool dfs(int u) {
for (int i = 0; i < eg[u].size(); i++) {
int v = eg[u][i];
if (!vis[v]) {
vis[v] = 1;
if (link[v] == -1 || dfs(link[v])) {
link[v] = u;
return 1;
}
}
}
return 0;
}
void solve() {
memset(s, 0, sizeof(s));
while (m--) {
scanf("%d%d", &u, &v);
s[u][v] = 1;
}
ans = inf;
for (int i = 1; i <= n; i++)
pos[i] = i;
do {
memset(link, -1, sizeof(link));
for (int i = 1; i <= n; i++) {
eg[i].clear();
for (int j = 1; j <= n; j++) {
u = pos[i];
if (i == 1)
v = pos[n];
else
v = pos[i - 1];
if (s[j][u] || s[j][v])
continue;
eg[i].push_back(j);
}
}
int now = 0;
for (int i = 1; i <= n; i++) {
memset(vis, 0, sizeof(vis));
now += dfs(i);
}
ans = min(ans, n - now);
} while (next_permutation(pos + 2, pos + n + 1) &&
ans); //一个柱子固定剩下(n-1)!的排列,因为是环!!
printf("%d\n", ans);
}
int main() {
while (~scanf("%d%d", &n, &m)) {
if (!n) {
puts("0");
continue;
}
solve();
}
return 0;
}
题解:这里是二分图匹配,不过要先对阴球进行全排列,枚举所有可能出现的位置,因为是环,所以先固定一个位置,对其他位置进行枚举即可;因为阴球已经固定位置,所以剩下来就是用ed数组记录下阳球在阴球旁边不会变se的阳球号码;所以之后就是对可行的阳球进行二分匹配,把所有能安全放上去得阳球都放上去,然后用n一减就得出放上去会变色的阳球的数量。题解链接
E - Ants
题目链接
题解:这里分为n个苹果树,n个蚁巢,要求一个蚁巢和一个苹果树之间形成一条路径,路径不能重复,苹果树和蚁巢只能用一次,路径的权值就是距离,求路径和最小。这题其实就是求二分图的最佳匹配,要用到km算法,是km算法的板子题。题解链接------算法链接