匹配主要就是二分图匹配和一般图匹配,一般图的带花树算法我还不会
二分图匹配主要就是匈牙利算法,也可以用最大流解决
具体的看blog:http://www.renfei.org/blog/bipartite-matching.html 讲的比较清楚了
今天做了几个简单题
hdu 1045
题意:给你个棋盘,一部分地方不能放,然后问你最多放多少个,同一行同一列不能有多个,不过如果两个同行或者同列之间隔着一个墙,就能放
题解:这题就是建图难,一开始想不到吧,可以把没有分开的一段行和列缩成一个点,然后如果这段行和某点列有相交部分,就是这两个点之间有边,然后最多放多少个棋子,就是最多取多少条边,就是二分图的最大匹配问题了
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 1005
#define MAXN 2000005
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define mid int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const LL mod = 1000000;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
int edge[20][20];
int head[MAX];
int row[5][5];
int col[5][5];
int match[MAX];
int used[MAX];
int tot;
int cnt;
int n;
void init(){
mem0(edge);
mem1(match);
mem0(col);
mem0(row);
tot=0;
cnt=0;
}
bool dfs(int u){
used[u]=1;
for(int i=1;i<=cnt;i++){
if(!edge[u][i]) continue;
int w=match[i];
if(w<0||(!used[w]&&dfs(w))){
match[i]=u;
match[u]=i;
return true;
}
}
return false;
}
int main(){
string s[5];
while(scanf("%d",&n)&&n){
init();
for(int i=0;i<n;i++) cin>>s[i];
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(s[i][j]=='.'&&j!=0&&s[i][j-1]=='.') row[i][j]=cnt;
else if(s[i][j]=='.') row[i][j]=++cnt;
}
}
for(int j=0;j<n;j++){
for(int i=0;i<n;i++){
if(s[i][j]=='.'&&i!=0&&s[i-1][j]=='.') col[i][j]=cnt;
else if(s[i][j]=='.') col[i][j]=++cnt;
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(s[i][j]=='.'){
edge[row[i][j]][col[i][j]]=edge[col[i][j]][row[i][j]]=1;
}
}
}
int ans=0;
for(int i=1;i<=cnt;i++){
if(match[i]<0){
mem0(used);
if(dfs(i)) ans++;
}
}
printf("%d\n",ans);
}
return 0;
}
题意:给你一些朋友关系,然后问把他们分成两组,每组中都不是朋友,第一组中每个人在第二组中都有一个朋友,明显就是二分图的最大匹配,不过就是可能给你的图有环,问你能不能构成二分图
题解:就是首先判断是否是二分图,这个用染色算法,就是dfs,如果搜到一条边上两端颜色相同就显然不是二分图。
判断完是二分图之后直接就匈牙利上了
数组貌似比较恶心,要开的大一点
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 1005
#define MAXN 2000005
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define mid int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const LL mod = 1000000;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
struct Edge{
int v,next;
}edge[1000005];
int head[MAX];
int col[MAX];
int match[MAX];
int used[MAX];
int tot;
void add_edge(int a,int b){
edge[tot]=(Edge){b,head[a]};
head[a]=tot++;
}
void init(){
mem1(head);
mem1(match);
mem0(col);
tot=0;
}
bool colour(int u){
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(!col[v]){
col[v]=-col[u];
if(!colour(v)) return false;
}
else if(col[v]==col[u]) return false;
}
return true;
}
bool dfs(int u){
used[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
int w=match[v];
if(w<0||(!used[w]&&dfs(w))){
match[v]=u;
match[u]=v;
return true;
}
}
return false;
}
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
init();
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
add_edge(a,b);
add_edge(b,a);
}
int flag=0;
for(int i=1;i<=n;i++){
if(!col[i]){
col[i]=1;
if(!colour(i)){
printf("No\n");
flag=1;
break;
}
}
}
if(!flag){
int ans=0;
for(int i=1;i<=n;i++){
if(match[i]<0){
mem0(used);
if(dfs(i)) ans++;
}
}
printf("%d\n",ans);
}
}
return 0;
}
hdu 1083
全裸的匈牙利一套带走
不说了
hdu 1281
题意:就是每行每列只能有一个车,然后有些点可以放车,不能放的点不影响(和第一题棋盘不同,我开头看错了)
重要点就是如果这个地方不放,那么就会影响最大数量
题解:有了第一题的经验,这题很明显就是行列缩成一个点,车所在的点就是行列相交的地方,就是行(点)和列(点)之间连接的边,然后墙壁不影响,更容易了,直接给你的坐标建图,然后先求出最大匹配,再枚举删除每条边,如果得到的匹配数不等于最大匹配,那么这个点就是重要点。
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 205
#define MAXN 2000005
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define mid int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const LL mod = 1000000;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
int edge[MAX][MAX];
int match[MAX];
int used[MAX];
int n,m,k;
void init(){
mem0(edge);
mem1(match);
}
bool dfs(int u){
used[u]=1;
for(int i=1;i<=n+m;i++){
if(!edge[u][i]) continue;
int w=match[i];
if(w<0||(!used[w]&&dfs(w))){
match[i]=u;
match[u]=i;
return true;
}
}
return false;
}
int main(){
int kase=0;
while(~scanf("%d%d%d",&n,&m,&k)){
init();
kase++;
for(int i=0;i<k;i++){
int a,b;
scanf("%d%d",&a,&b);
edge[a][b+n]=edge[b+n][a]=1;
}
int ans=0;
for(int i=1;i<=n+m;i++){
if(match[i]<0){
mem0(used);
if(dfs(i)) ans++;
}
}
int ret=0;
for(int i=1;i<=n+m;i++){
for(int j=i+1;j<=n+m;j++){
if(edge[i][j]){
edge[i][j]=edge[j][i]=0;
mem1(match);
int tmp=0;
for(int k=1;k<=n+m;k++){
if(match[k]<0){
mem0(used);
if(dfs(k)) tmp++;
}
}
if(tmp!=ans) ret++;
edge[i][j]=edge[j][i]=1;
}
}
}
printf("Board %d have %d important blanks for %d chessmen.\n",kase,ret,ans);
}
return 0;
}
hdu 2819
题意:给你一个01矩阵,问你能不能通过行列交换,使对角线都是1
题解:首先要知道,如果i行j列是1,那么就是点i->j+n有边,所以对角线都为1,就是说最多可以连n条边,就是最大匹配是n
把行和列缩点,求出最大匹配,如果是n,然后就是看交换多少次,要知道变成对角线为1,只用行变换或者只用列变换都可以做到,所以枚举每行,看对应的行match的是不是这一列,如果不是就找下面满足的行和它换(其实挺水的,一开始没认真做,题目都没怎么搞懂)
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 205
#define MAXN 2000005
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define mid int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const LL mod = 1000000;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
struct Edge{
int v,next;
}edge[20005];
int head[MAX];
int row[MAX];
int match[MAX];
int used[MAX];
int tot;
void add_edge(int a,int b){
edge[tot]=(Edge){b,head[a]};
head[a]=tot++;
}
void init(){
mem1(head);
mem1(match);
mem0(row);
tot=0;
}
bool dfs(int u){
used[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
int w=match[v];
if(w<0||(!used[w]&&dfs(w))){
match[v]=u;
match[u]=v;
return true;
}
}
return false;
}
int main(){
int n;
while(~scanf("%d",&n)){
init();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int a;
scanf("%d",&a);
if(a){
add_edge(i,j+n);
add_edge(j+n,i);
}
}
}
int ans=0;
for(int i=1;i<=2*n;i++){
if(match[i]<0){
mem0(used);
if(dfs(i)) ans++;
}
}
if(ans!=n) printf("-1\n");
else{
int ret=0;
for(int i=1;i<=n;i++){
int k=match[i]-n;
if(k==i) continue;
for(int j=i+1;j<=n;j++){
int t=match[j]-n;
if(t==i){
ret++;
row[i]=j;
swap(match[i],match[j]);
}
}
}
printf("%d\n",ret);
for(int i=1;i<=n;i++){
if(row[i]) printf("R %d %d\n",i,row[i]);
}
}
}
return 0;
}
hdu 2389
题意:就是给你6000个点,分成两堆,给你坐标,然后给你人的速度,如果在时间内这个人能跑到伞所在的坐标,那么这两个点之间就有边,这样的话最多有3000×3000的边
匈牙利就会TLE,用网络流的ISAP要建反向边1800W伤不起,所以就去学了HK算法,详见http://www.cnblogs.com/penseur/archive/2013/06/16/3138981.html
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 6005
#define MAXN 2000005
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define mid int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const LL mod = 1000000;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
/**************读入外挂**********************/
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}while(isdigit(tmp=getchar()));
return ret;
}
/*******************************************/
struct Edge{
int v,next;
}edge[10000005];
struct Node{
int x,y,z;
}node[MAX];
int head[MAX];
int match[MAX];
int used[MAX];
int dis[MAX];
int tot;
int T,t,n,m,diss;
void add_edge(int a,int b){
edge[tot]=(Edge){b,head[a]};
head[a]=tot++;
}
void init(){
mem1(head);
mem1(match);
tot=0;
}
bool bfs(){
diss=INF;
mem1(dis);
queue<int> q;
for(int i=1;i<=n;i++){
if(match[i]==-1){
dis[i]=0;
q.push(i);
}
}
while(!q.empty()){
int u=q.front();
q.pop();
if(dis[u]>diss) break;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(dis[v]==-1){
dis[v]=dis[u]+1;
if(match[v]==-1) diss=dis[v];
else {
dis[match[v]]=dis[v]+1;
q.push(match[v]);
}
}
}
}
return diss!=INF;
}
bool dfs(int u){
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(!used[v]&&dis[v]==dis[u]+1){
used[v]=1;
if(match[v]!=-1&&diss==dis[v]) continue;
if(match[v]==-1||dfs(match[v])){
match[u]=v;
match[v]=u;
return true;
}
}
}
return false;
}
int MaxMatch(){
int ans=0;
while(bfs()){
mem0(used);
for(int i=1;i<=n;i++){
if(match[i]==-1&&dfs(i)) ans++;
}
}
return ans;
}
int main(){
scanf("%d",&T);
for(int kase=1;kase<=T;kase++){
scanf("%d%d",&t,&n);
init();
for(int i=1;i<=n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
node[i]=(Node){a,b,c};
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
for(int j=1;j<=n;j++){
int aa=node[j].x-a;
int bb=node[j].y-b;
int cc=t*node[j].z;
if(cc*cc>=aa*aa+bb*bb){
add_edge(j,i+n);
}
}
}
printf("Scenario #%d:\n%d\n\n",kase,MaxMatch());
}
return 0;
}
题解:套一发模板就好了