必做题
P1525 [NOIP2010 提高组] 关押罪犯
思路:和 P1330 封锁阳光大学 类似,用领接表存边(开两倍大小不然会爆)。二分查找最小最大怨气值,对于小于这个值的边,视作不存在(也就是放在那个监狱无所谓),对于其他边,通过类似于 P1330 的染色法,判断是否能形成一个二分图(每一个连通分量里被染成不同颜色的点位于二分图不同的一边)
完整代码如下:
#include<bits/stdc++.h>
using namespace std;
#define N 20005
#define M 100005
int n,m,u,v,w,tmp,ans;
struct edge{
int to,nex,val;
}e[2*M]; // 注意是无向图
int head[M],cnt=0;
int dye[N];
queue<int> q;
void add_edge(int from ,int to, int val)
{
e[++cnt].to = to;
e[cnt].val = val;
e[cnt].nex = head[from];
head[from] = cnt;
}
bool bfs_check(int mid)
{
memset(dye,0,sizeof(dye));
while(!q.empty()) q.pop();
for(int i=1 ; i<=n ; i++){
if(dye[i]) continue;
dye[i] = 1;
q.push(i);
while(!q.empty()){
tmp = q.front();
q.pop();
for(int j=head[tmp] ; j ; j=e[j].nex){
if(e[j].val<=mid) continue; // 这些边是合法的,无所谓放在哪个监狱
if(dye[e[j].to]==0){
dye[e[j].to] = dye[tmp]==1? 2 : 1;
q.push(e[j].to);
}
else if(dye[e[j].to]==dye[tmp]) return false;
}
}
}
return true;
}
int main()
{
// freopen("D:\\test.txt","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1 ; i<=m ; i++){
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);
add_edge(v,u,w);
}
int lef=0, rig=1e9+5, mid;
while(lef<=rig){
mid=lef+(rig-lef)/2;
if(bfs_check(mid)){
ans = mid;
rig=mid-1;
}
else lef=mid+1;
}
printf("%d",ans);
return 0;
}
P3386 【模板】二分图最大匹配
关键词:Hungary
算法
完整代码如下:
#include<bits/stdc++.h>
using namespace std;
#define N 505
int n,m,e,u,v;
bool Adjacency_Matrix[N][N]; // 邻接矩阵
int link_of_v[N]; // 记录右点连接的左点是谁
bool used_of_v[N]; // 右点是否访问过,主要是为了dfs找新的右点用
bool dfs(int u)
{
for(int i=1 ; i<=m ; i++){
if(Adjacency_Matrix[u][i] && !used_of_v[i]){
used_of_v[i] = true;
if(link_of_v[i]==-1 || dfs(link_of_v[i])){
link_of_v[i] = u;
return true;
}
}
}
return false;
}
int hungary()
{
int res=0;
for(int i=1 ; i<=n ; i++){
memset(used_of_v,0,sizeof(used_of_v));
// 勿忘!对于每一个u来说所有v一开始都是没访问过的
if(dfs(i)) ++res;
}
return res;
}
int main()
{
// freopen("D:\\test.txt","r",stdin);
memset(link_of_v,-1,sizeof(link_of_v));
scanf("%d%d%d",&n,&m,&e);
for(int i=1 ; i<=e ; i++){
scanf("%d%d",&u,&v);
Adjacency_Matrix[u][v] = true; // 注意这不是一个无向图!
}
printf("%d",hungary());
return 0;
}
P1129 [ZJOI2007] 矩阵游戏
思路:直接把输入的矩阵当成邻接矩阵(把行和列分开,当成一个二分图),用 Hungary
算法 求最大匹配数,如果和
n
n
n 相同就说明Yes,否则No
完整代码如下:
#include<bits/stdc++.h>
using namespace std;
#define N 205
int t,n;
int link_of_v[N];
bool used_of_v[N];
bool Matrix[N][N];
bool dfs(int u)
{
for(int i=1 ; i<=n ; i++){
if(Matrix[u][i] && !used_of_v[i]){
used_of_v[i] = true;
if(link_of_v[i]==-1 || dfs(link_of_v[i])){
link_of_v[i] = u;
return true;
}
}
}
return false;
}
int hungary()
{
int res=0;
for(int i=1 ; i<=n ; i++){
memset(used_of_v,0,sizeof(used_of_v));
if(dfs(i)) res++;
}
return res;
}
int main()
{
// freopen("D:\\test.txt","r",stdin);
scanf("%d",&t);
for(int i=0 ; i<t ; i++){
memset(link_of_v,-1,sizeof(link_of_v));
scanf("%d",&n);
for(int r=1 ; r<=n ; r++)
for(int c=1 ; c<=n ; c++) scanf("%d",&Matrix[r][c]);
if(n == hungary()) printf("Yes\n");
else printf("No\n");
}
return 0;
}