棋盘游戏 HDU - 1281 题目链接
行与列的二分匹配,暴力删除每一点求最新的二分匹配,比原先的小那就是重要点
#include <bits/stdc++.h>
using namespace std;
const int N = 100 + 5;
struct node{
int x,y;
}s[N * N];
int Map[N][N],vis[N],link[N];
int cnt[N];
bool dfs(int x,int m)
{
for (int i = 1; i <= m; i++){
if (!Map[x][i]) continue;
if (vis[i]) continue;
vis[i] = 1;
if (!link[i] || dfs(link[i],m)){
link[i] = x;
return true;
}
}
return false;
}
int main()
{
int n,m,k,Case = 1;
while(scanf("%d %d %d",&n,&m,&k) == 3){
memset(Map,0,sizeof(Map));
memset(cnt,0,sizeof(cnt));
for (int i = 0; i < k; i++){
scanf("%d %d",&s[i].x,&s[i].y);
Map[s[i].x][s[i].y] = 1;
cnt[s[i].x]++;
}
memset(link,0,sizeof(link));
int mx = 0;
for (int i = 1; i <= n; i++){
memset(vis,0,sizeof(vis));
if (cnt[i]){
mx += dfs(i,m);
}
}
int ans = 0;
for (int i = 0; i < k; i++){
Map[s[i].x][s[i].y] = 0;
cnt[s[i].x]--;
memset(link,0,sizeof(link));
int tem = 0;
for (int i = 1; i <= n; i++){
memset(vis,0,sizeof(vis));
if (cnt[i]){
tem += dfs(i,m);
}
}
Map[s[i].x][s[i].y] = 1;
cnt[s[i].x]++;
if (tem < mx) ans++;
}
printf("Board %d have %d important blanks for %d chessmen.\n",Case++,ans,mx);
}
return 0;
}
Swap HDU - 2819 题目链接
题意
交换矩阵中的行或列,使矩阵的对角线都为1
思路
行列的二分图匹配
最后把每一列都换到对应位置即可
#include <bits/stdc++.h>
using namespace std;
const int N = 100 + 5;
int a[N][N];
int link[N],vis[N];
vector<pair<int,int> >p;
bool dfs(int x,int n)
{
for (int i = 1; i <= n; i++){
if (!a[x][i]) continue;
if (vis[i]) continue;
vis[i] = 1;
if (!link[i] || dfs(link[i],n)){
link[i] = x;
return true;
}
}
return false;
}
int main()
{
int n;
while(scanf("%d",&n) == 1){
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
scanf("%d",&a[i][j]);
}
}
memset(link,0,sizeof(link));
int cnt = 0;
for (int i = 1; i <= n; i++){
memset(vis,0,sizeof(vis));
cnt += dfs(i,n);
}
if (cnt < n) cout << -1 << endl;
else {
p.clear();
for (int i = 1; i <= n; i++){
int x = 1;
while(link[x] != i) x++;
if (x == i) continue;
swap(link[x],link[i]);
p.push_back({i,x});
}
cout << p.size() << endl;
for (auto &k : p){
cout << "C " << k.first << " " << k.second << endl;
}
}
}
return 0;
}
无题II HDU 2236 题目链接
题意
这是一个简单的游戏,在一个n*n的矩阵中,找n个数使得这n个数都在不同的行和列里并且要求这n个数中的最大值和最小值的差值最小。
思路
二分匹配差值
#include <bits/stdc++.h>
using namespace std;
const int N = 111;
const int INF = 0x3f3f3f3f;
int g[N][N];
int n;
int link[N],vis[N];
int mid,k;
bool dfs(int x)
{
for (int i = 1; i <= n; i++){
if (vis[i]) continue;
if (g[x][i] < k || g[x][i] > k + mid) continue;
vis[i] = 1;
if (!link[i] || dfs(link[i])){
link[i] = x;
return true;
}
}
return false;
}
bool solve()
{
memset(link,0,sizeof(link));
for (int i = 1; i <= n; i++){
memset(vis,0,sizeof(vis));
if (!dfs(i)) return false;
}
return true;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
int mx = -INF,mi = INF;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
scanf("%d",&g[i][j]);
mx = max(mx,g[i][j]);
mi = min(mi,g[i][j]);
}
}
int l = 0,r = mx - mi;
int ans = r;
while(l <= r){
mid = (l + r) >> 1;
bool flag = true;
for (k = mi; k + mid <= mx; k++){
if (solve()){
ans = mid;
r = mid - 1;
flag = false;
break;
}
}
if (flag) l = mid + 1;
}
cout << ans << endl;
}
return 0;
}
Rain on your Parade HDU - 2389 题目链接
思路
匈牙利算法优化
Hopcroft-Karp算法模板题
#include <bits/stdc++.h>
using namespace std;
const int N = 3e3 + 5;
const int INF = 0x3f3f3f3f;
struct node{
int x,y,v;
}s[N];
struct node2{
int x,y;
}p[N];
int linku[N],linkv[N],vis[N];
int du[N],dv[N];
int dis;
vector<int> ve[N];
bool bfs(int n)
{
memset(du,0,sizeof(du));
memset(dv,0,sizeof(dv));
queue<int> q;
dis = INF;
for (int i = 1; i <= n; i++){
if (!linku[i]){
q.push(i);
du[i] = 0;
}
}
while(!q.empty()){
int u = q.front();
q.pop();
if (du[u] > dis) break;
for (auto v : ve[u]){
if (!dv[v]){
dv[v] = du[u] + 1;
if (!linkv[v]){
dis = dv[v];
}
else {
du[linkv[v]] = dv[v] + 1;
q.push(linkv[v]);
}
}
}
}
return dis != INF;
}
bool dfs(int u)
{
for (auto v : ve[u]){
if (!vis[v] && dv[v] == du[u] + 1){
vis[v] = 1;
if (linkv[v] && dis == dv[v]) continue;
if (!linkv[v] || dfs(linkv[v])){
linku[u] = v;
linkv[v] = u;
return true;
}
}
}
return false;
}
int solve(int n)
{
int ans = 0;
memset(linku,0,sizeof(linku));
memset(linkv,0,sizeof(linkv));
while(bfs(n)){
memset(vis,0,sizeof(vis));
for (int i = 1; i <= n; i++){
if (!linku[i] && dfs(i)) ans++;
}
}
return ans;
}
int calc(int a,int b,int c,int d)
{
return (a - b) * (a - b) + (c - d) * (c - d);
}
int main()
{
int t,Case = 1;
scanf("%d",&t);
while(t--){
int rain;
scanf("%d",&rain);
int n,m;
scanf("%d",&n);
for (int i = 1; i <= n; i++){
scanf("%d %d %d",&s[i].x,&s[i].y,&s[i].v);
s[i].v *= rain;
s[i].v *= s[i].v;
}
scanf("%d",&m);
for (int i = 1; i <= m; i++){
scanf("%d %d",&p[i].x,&p[i].y);
}
for (int i = 0; i < N; i++){
ve[i].clear();
}
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
int tem = calc(s[i].x,p[j].x,s[i].y,p[j].y);
if (tem <= s[i].v) ve[i].push_back(j);
}
}
printf("Scenario #%d:\n%d\n\n",Case++,solve(n));
}
return 0;
}
Strategic Game HDU - 1054 题目链接
题意
有一个树,在一个节点上放士兵可以观察与其相连的所有点,问最少需要几个士兵就可以观察到全部节点
思路
(最小顶点覆盖数)
#include <cstring>
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 1555;
const int INF = 0x3f3f3f3f;
int vis[N];
int link[N];
vector<int> ve[N];
bool dfs(int u)
{
for (int i = 0; i < (int)ve[u].size(); i++){
int v = ve[u][i];
if (vis[v]) continue;
vis[v] = 1;
if (link[v] == - 1 || dfs(link[v])){
link[u] = v;
link[v] = u;
return true;
}
}
return false;
}
int main()
{
int n;
while(scanf("%d",&n) == 1){
for (int i = 0; i <= n; i++){
ve[i].clear();
}
for (int i = 0; i < n; i++){
int u,m;
scanf("%d:(%d)", &u, &m);
for (int j = 0; j < m; j++){
int v;
scanf("%d",&v);
ve[u].push_back(v);
ve[v].push_back(u);
}
}
int ans = 0;
memset(link,-1,sizeof(link)); /// 节点0开始
for (int i = 0; i < n; i++){
memset(vis,0,sizeof(vis));
if (link[i] == -1 && dfs(i)) ans++;
}
printf("%d\n",ans);
}
return 0;
}
Air Raid HDU - 1151 题目链接
题意
在一个城镇,有m个路口,和n条路,这些路都是单向的,而且路不会形成环,现在要弄一些伞兵去巡查这个城镇,伞兵只能沿着路的方向走,问最少需要多少伞兵才能把所有的路口搜一遍。
(点不能重复经过)
思路
有向图的最大匹配
#include <cstring>
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 155;
const int INF = 0x3f3f3f3f;
int vis[N];
int link[N];
vector<int> ve[N];
bool dfs(int u)
{
for (int i = 0; i < (int)ve[u].size(); i++){
int v = ve[u][i];
if (vis[v]) continue;
vis[v] = 1;
if (!link[v] || dfs(link[v])){
link[v] = u;
return true;
}
}
return false;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d %d",&n,&m);
for (int i = 1; i <= n; i++){
ve[i].clear();
}
for (int i = 0; i < m; i++){
int u,v;
scanf("%d %d",&u,&v);
ve[u].push_back(v);
}
int ans = 0;
memset(link,0,sizeof(link));
for (int i = 1; i <= n; i++){
memset(vis,0,sizeof(vis));
if (dfs(i)) ans++;
}
printf("%d\n",n - ans);
}
return 0;
}
Treasure Exploration POJ - 2594 题目链接
题意
hdu1151 的改版,点可以重复经过
思路
使用floyd重新构图
最小路径覆盖 = 总点数 - 最大匹配数
#include <cstring>
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 555;
const int INF = 0x3f3f3f3f;
int vis[N];
int link[N];
int mp[N][N];
bool dfs(int u,int n)
{
for (int v = 1; v <= n; v++){
if (!mp[u][v] || v == u) continue;
if (vis[v]) continue;
vis[v] = 1;
if (!link[v] || dfs(link[v],n)){
link[v] = u;
return true;
}
}
return false;
}
void floyd(int n)
{
for (int k = 1; k <= n; k++){
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
if (mp[i][k] && mp[k][j]){
mp[i][j] = 1;
}
}
}
}
}
int main()
{
int n,m;
while(scanf("%d %d",&n,&m) == 2 && (n || m)){
memset(mp,0,sizeof(mp));
for (int i = 0; i < m; i++){
int u,v;
scanf("%d %d",&u,&v);
mp[u][v] = 1;
}
floyd(n);
int ans = 0;
memset(link,0,sizeof(link));
for (int i = 1; i <= n; i++){
memset(vis,0,sizeof(vis));
if (dfs(i,n)) ans++;
}
printf("%d\n",n - ans);
}
return 0;
}
Cat VS Dog HDU - 3829题目链接
题意
动物园有N只猫,M只狗,P个小孩。每个小孩都有自己喜欢的动物和讨厌的动物,如果他喜欢狗,那么就讨厌猫, 如果他讨厌狗,那么他就喜欢猫。某个小孩能开心,当且仅当他喜欢的动物留在动物园和讨厌的动物不在动物园里面。 现让管理员通过带走某些动物,问最多能使多少个孩子开心。
思路
最大独立集 = 总点数 - 最大匹配数
#include <cstring>
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 555;
const int INF = 0x3f3f3f3f;
struct node{
char s1[5],s2[5];
}s[N];
int vis[N];
int link[N];
vector<int> ve[N];
bool dfs(int u)
{
for (int i = 0; i < (int)ve[u].size(); i++){
int v = ve[u][i];
if (vis[v]) continue;
vis[v] = 1;
if (!link[v] || dfs(link[v])){
link[v] = u;
link[u] = v;
return true;
}
}
return false;
}
int main()
{
int n,m,k;
while(scanf("%d %d %d",&n,&m,&k) == 3){
for (int i = 0; i < k; i++){
scanf("%s %s",s[i].s1,s[i].s2);
}
for (int i = 1; i <= k; i++){
ve[i].clear();
}
for (int i = 0; i < k; i++){
for (int j = i + 1; j < k; j++){
if (strcmp(s[i].s2,s[j].s1) == 0 || strcmp(s[i].s1,s[j].s2) == 0){
ve[i + 1].push_back(j + 1);
ve[j + 1].push_back(i + 1);
}
}
}
int ans = 0;
memset(link,0,sizeof(link));
for (int i = 1; i <= k; i++){
memset(vis,0,sizeof(vis));
if (!link[i] && dfs(i)) ans++;
}
printf("%d\n",k - ans);
}
return 0;
}
HDU 3081 题目链接
题意
n个男生,n个女生,接下来有 m个关系,u v表示第 u 个女生和第 v个男生可以配对,然后接下来有 f 个关系,u v表示第 u个女生和第v个女生是好友,如果 u 和 v可以配对,u 和 w是好友,那么w 和 v也是可以配对的.问知道这些人的关系,最多可以完全配对多少次(完全配对是指n个男生和n个女生都可以配对)?
思路
floyd建图,然后求完美匹配,匹配完成后就删边,直到不能匹配为止
(这道题是在网络流专题里的,使用二分和并查集最大流也可以解)
#include <cstring>
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 222;
const int INF = 0x3f3f3f3f;
int vis[N];
int pre[N];
int link[N];
int g[N][N];
int n,m,f;
bool dfs(int u)
{
for (int v = 1; v <= n; v++){
if (!g[u][v]) continue;
if (vis[v]) continue;
vis[v] = 1;
if (!link[v] || dfs(link[v])){
link[v] = u;
return true;
}
}
return false;
}
int Find(int x)
{
return pre[x] == x ? x : pre[x] = Find(pre[x]);
}
void Union(int a,int b)
{
int x = Find(a),y = Find(b);
if (x != y){
pre[x] = y;
}
}
int solve()
{
memset(link,0,sizeof(link));
for (int i = 1; i <= n; i++){
memset(vis,0,sizeof(vis));
if (!dfs(i)) return false;
}
return true;
}
void build()
{
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
if (Find(i) == Find(j)){
for (int k = 1; k <= n; k++){
if (g[i][k]) g[j][k] = 1;
}
}
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d %d %d",&n,&m,&f);
memset(g,0,sizeof(g));
for (int i = 0; i < m; i++){
int u,v;
scanf("%d %d",&u,&v);
g[u][v] = 1;
}
for (int i = 1; i <= n; i++){
pre[i] = i;
}
for (int i = 0; i < f; i++){
int u,v;
scanf("%d %d",&u,&v);
Union(u,v);
}
build();
int ans = 0;
while(true){
if (!solve()) break;
ans++;
for (int i = 1; i <= n; i++){
g[link[i]][i] = 0;
}
}
printf("%d\n",ans);
}
return 0;
}