匈牙利求最大匹配数模板
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int dfs(int x) { for (int i=last[x];i;i=nxt[i]) { if (!used[to[i]]) { used[to[i]]=1; if (!match[to[i]] || dfs(match[to[i]])) { match[to[i]]=x; return 1; } } } return 0; }
KM最大权匹配模板
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int dfs(int x) { int i,tmp; vis_g[x]=1; for (i=1;i<=n;i++) { if (vis_b[i]) continue; tmp=ex_g[x]+ex_b[i]-mp[x][i]; if (tmp==0) { vis_b[i]=1; if (!match[i] || dfs(match[i])) { match[i]=x; return 1; } } else slack[i]=min(slack[i],tmp); } return 0; } int km() { int i,j,re=0,tmp; for (i=1;i<=n;i++) { ex_g[i]=0;ex_b[i]=0; match[i]=0; for (j=1;j<=n;j++) ex_g[i]=max(ex_g[i],mp[i][j]); } for (i=1;i<=n;i++) { for (j=1;j<=n;j++) slack[j]=inf; while (1) { memset(vis_g,0,sizeof(vis_g)); memset(vis_b,0,sizeof(vis_b)); if (dfs(i)) break; tmp=inf; for (j=1;j<=n;j++) if (!vis_b[j]) tmp=min(tmp,slack[j]); for (j=1;j<=n;j++) { if (vis_g[j]) ex_g[j]-=tmp; if (vis_b[j]) ex_b[j]+=tmp; else slack[j]-=tmp; } } } for (i=1;i<=n;i++) re+=mp[match[i]][i]; return re; }
C HDU 1045
建图+最大匹配。
我们为每一行中被墙隔开的每一部分编上序号,为每一列中被墙隔开的每一部分也编上序号。对于每一个单位,当它被安上炮楼时意味着它对应的“行”和“列”(处理后的行和列)内不可放置炮楼,因此在每一个单位的“行”序号和“列”序号之间连边,求出的最大匹配就是能放置炮楼的最大数。(这题的代码几乎和E题一模一样)
不过因为数据很小(n<=4),好像也可以直接搜索。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstring> #define maxn 20 #define maxm 20 using namespace std; int num,last[maxn],to[maxm],nxt[maxm],used[maxn],match[maxn],cnt; int a[maxn][maxn],b[maxn][maxn],cnta,cntb; char mp[maxn][maxn]; int dfs(int x) { for (int i=last[x];i;i=nxt[i]) { if (!used[to[i]]) { used[to[i]]=1; if (!match[to[i]] || dfs(match[to[i]])) { match[to[i]]=x; return 1; } } } return 0; } void add(int x,int y) { to[++num]=y;nxt[num]=last[x];last[x]=num; } int main() { int i,j,n,ans; while (scanf("%d",&n)!=EOF && n!=0) { for (i=1;i<=n;i++) scanf("%s",mp[i]+1); cnta=0;cntb=0;ans=0;num=0; memset(match,0,sizeof(match)); memset(last,0,sizeof(last)); for (i=1;i<=n;i++) for (j=1;j<=n;j++)//竖向 { if (mp[i][j]=='X') continue; if (i!=1 && mp[i-1][j]=='.') a[i][j]=a[i-1][j]; else a[i][j]=++cnta; } for (i=1;i<=n;i++) for (j=1;j<=n;j++)//横向 { if (mp[i][j]=='X') continue; if (j!=1 && mp[i][j-1]=='.') b[i][j]=b[i][j-1]; else b[i][j]=++cntb; add(a[i][j],b[i][j]); } for (i=1;i<=cnta;i++) { memset(used,0,sizeof(used)); ans+=dfs(i); } printf("%d ",ans); } return 0; }
D HDU 2448
E POJ 2226
建图+最小顶点覆盖。
对于每个一个单位的水洼,它既可以被横着的木板遮盖,也可以被竖着的木板遮盖。于是我们将每一条横着遮盖水洼的木板、每一条竖着遮盖水洼的木板分别编上号,对于每个一个单位的水洼在能够遮盖它的横木板和竖木板之间连边,可以发现这条边代表了这个水洼。由于对于每个水洼(边)要有一条木板(该边的两端点)遮盖,问题便转化为了求这个二分图的最小顶点覆盖。由于最小顶点覆盖等于最大匹配,于是这道题就可以当作二分图匹配做了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstring> #define maxn 2505 #define maxm 2505 using namespace std; int num,last[maxn],to[maxm],nxt[maxm],used[maxn],match[maxn],cnt; int a[maxn][maxn],b[maxn][maxn],cnta,cntb; char mp[maxn][maxn]; int dfs(int x) { for (int i=last[x];i;i=nxt[i]) { if (!used[to[i]]) { used[to[i]]=1; if (!match[to[i]] || dfs(match[to[i]])) { match[to[i]]=x; return 1; } } } return 0; } void add(int x,int y) { to[++num]=y;nxt[num]=last[x];last[x]=num; } int main() { int i,j,n,m,ans; while (scanf("%d%d",&n,&m)!=EOF) { for (i=1;i<=n;i++) scanf("%s",mp[i]+1); cnta=0;cntb=0;ans=0;num=0; memset(match,0,sizeof(match)); memset(last,0,sizeof(last)); for (i=1;i<=n;i++) for (j=1;j<=m;j++)//竖向 { if (mp[i][j]!='*') continue; if (i!=1 && mp[i-1][j]=='*') a[i][j]=a[i-1][j]; else a[i][j]=++cnta; } for (i=1;i<=n;i++) for (j=1;j<=m;j++)//横向 { if (mp[i][j]!='*') continue; if (j!=1 && mp[i][j-1]=='*') b[i][j]=b[i][j-1]; else b[i][j]=++cntb; add(a[i][j],b[i][j]); } for (i=1;i<=cnta;i++) { memset(used,0,sizeof(used)); ans+=dfs(i); } printf("%d ",ans); } return 0; }
F HDU 3718
H POJ 2060
I HDU 2819
J HDU 2853