UVA11419 SAM I AM 我是 SAM

题面


题面:给出一个\(R\times C\)大小的网格,网格上面放了一些目标。可以在网格外发射子弹,子弹会沿着垂直或者水平方向飞行,并且打掉飞行路径上的所有目标,如图所示。你的任务是计算出最少需要多少子弹,各从哪些位置发射,才能把所有目标全部打掉。


感觉这就是典型的算法模板题,第一次可能想不出来,第二次就会了。


建立一个二分图:左部点代表每一行,右部点代表每一列。如果一个目标在第\(i\)行第\(j\)列,就连一条\(i \to j\)的边。
那么我们的目标是选择最少的点,使每一条边至少有一个端点被选中,即二分图的最小点覆盖。根据König定理,最小点覆盖数=最大匹配数,所以直接跑匈牙利算法即可。
但关于输出方案我还没有看懂,待以后补上。


这里给两篇参考题解:
https://www.cnblogs.com/colazcy/p/11515004.html
https://blog.csdn.net/z309241990/article/details/34417693

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<queue>
#include<assert.h>
#include<ctime>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
#define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 1e3 + 5;
const int maxe = 1e6 + 5;
In ll read()
{
	ll ans = 0;
	char ch = getchar(), las = ' ';
	while(!isdigit(ch)) las = ch, ch = getchar();
	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
	if(las == '-') ans = -ans;
	return ans;
}
In void write(ll x)
{
	if(x < 0) x = -x, putchar('-');
	if(x >= 10) write(x / 10);
	putchar(x % 10 + '0');
}
In void MYFILE()
{
#ifndef mrclr
	freopen("ha.in", "r", stdin);
	freopen("ha.out", "w", stdout);
#endif
}

int n, m, K;
struct Edge
{
	int nxt, to;
}e[maxe];
int head[maxn], ecnt = -1;
In void addEdge(int x, int y)
{
	e[++ecnt] = (Edge){head[x], y};
	head[x] = ecnt;
}

bool vx[maxn], vy[maxn];
int lft[maxn], rgt[maxn];
In bool dfs(int now)
{
	vx[now] = 1;
	forE(i, now, v) if(!vy[v])
	{
		vy[v] = 1;
		if(!lft[v] || dfs(lft[v])) {lft[v] = now; rgt[now] = v; return 1;}
	}
	return 0;
}
In int hung()
{
	int ret = 0; Mem(lft, 0), Mem(rgt, 0);
	for(int i = 1; i <= n; ++i)
	{
		Mem(vx, 0), Mem(vy, 0);
		if(dfs(i)) ++ret;
	}
	return ret;
}
In void Find(int ans)
{
	Mem(vx, 0), Mem(vy, 0); 
	for(int i = 1; i <= n; ++i) if(!rgt[i]) dfs(i);
	write(ans);
	for(int i = 1; i <= n; ++i) if(!vx[i]) space, putchar('r'), write(i);
	for(int i = 1; i <= m; ++i) if(vy[i]) space, putchar('c'), write(i);
	enter;
}

int main()
{
//	MYFILE();
	while(scanf("%d%d%d", &n, &m, &K) && (n | m | K))
	{
		Mem(head, -1), ecnt = -1;
		for(int i = 1; i <= K; ++i) 
		{
			int x = read(), y = read();
			addEdge(x, y);
		}
		Find(hung());		
	}
	return 0;
}
上一篇:《Android应用开发》——1.3节配置Eclipse


下一篇:入门篇:函数计算中角色和访问策略的讲解