P1525 [NOIP2010 提高组] 关押罪犯
题目描述
S 城现有两座监狱,一共关押着 N 名罪犯,编号分别为 1∼N。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为 c 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为 c 的冲突事件。
每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到 S 城 Z 市长那里。公务繁忙的 Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。
在详细考察了 N 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。
那么,应如何分配罪犯,才能使 Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?
输入格式
每行中两个数之间用一个空格隔开。第一行为两个正整数 N,M,分别表示罪犯的数目以及存在仇恨的罪犯对数。接下来的 M 行每行为三个正整数 aj, bj, cj,表示 aj 号和 bj 号罪犯之间存在仇恨,其怨气值为 cj。数据保证 1<aj ≤bj ≤N,0<cj ≤10^9,且每对罪犯组合只出现一次。
输出格式
共一行,为 Z 市长看到的那个冲突事件的影响力。如果本年内监狱中未发生任何冲突事件,请输出 0。
#include<bits/stdc++.h> #define MAXN 100015 using namespace std; int n,m,sum; int head[MAXN]; struct node { int to; int z; int next; }edge[MAXN*2]; void add(int x,int y,int z) { edge[++sum].next=head[x]; edge[sum].to=y; edge[sum].z=z; head[x]=sum; } bool bfs(int midd) { int color[MAXN]; memset(color,0,sizeof(color)); queue <int> q; for (int j=1;j<=n;j++) { if (color[j]) continue; q.push(j);color[j]=1; do { int k=q.front();q.pop(); for (int i=head[k];i;i=edge[i].next) if (edge[i].z>=midd) { if (color[edge[i].to]==0) { color[edge[i].to]=color[k]==1?2:1; q.push(edge[i].to); } else if (color[edge[i].to]==color[k]) return false; } }while (!q.empty()); } return true; } int main() { int maxx; cin>>n>>m; for (int i=1;i<=m;i++) { int a,b,c; cin>>a>>b>>c; maxx=max(maxx,c); add(a,b,c); add(b,a,c); } int l=0,r=maxx+1,midd; while (l+1<r) { midd=(l+r)>>1; if (bfs(midd)) r=midd; else l=midd; } printf("%d",l); return 0; }
P1129 [ZJOI2007] 矩阵游戏
题目描述
小 Q 是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏――矩阵游戏。矩阵游戏在一个 n×n 黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作:
行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)。
列交换操作:选择矩阵的任意两列,交换这两列(即交换对应格子的颜色)。
游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑色。
对于某些关卡,小 Q 百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!于是小 Q 决定写一个程序来判断这些关卡是否有解。
输入格式
本题单测试点内有多组数据。
第一行包含一个整数 T,表示数据的组数,对于每组数据,输入格式如下:
第一行为一个整数,代表方阵的大小 n。 接下来 n 行,每行 n 个非零即一的整数,代表该方阵。其中 0 表示白色,1 表示黑色。
输出格式
对于每组数据,输出一行一个字符串,若关卡有解则输出 Yes,否则输出 No。
#include<bits/stdc++.h> #define MAXN 505 using namespace std; int n,ans,t; int matched[MAXN]; bool G[MAXN][MAXN],vis[MAXN]; bool dfs(int x) { for(int i=1;i<=n;i++) if(G[x][i]&&!vis[i]) { vis[i]=true; if(!matched[i]||dfs(matched[i])) { matched[i]=x; return true; } } return false; } int main() { scanf("%d",&t); while(t--) { memset(matched,0,sizeof(matched)); memset(G,false,sizeof(G)); ans=0; scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&G[i][j]); for(int i=1;i<=n;i++) { memset(vis,false,sizeof(vis)); ans+=dfs(i); } if(ans==n) printf("Yes\n"); else printf("No\n"); } return 0; }
P3386 【模板】二分图最大匹配
题目描述
给定一个二分图,其左部点的个数为 n,右部点的个数为 m,边数为 e,求其最大匹配的边数。
左部点从 1 至 n 编号,右部点从 1 至 m 编号。
输入格式
输入的第一行是三个整数,分别代表 n,m 和 e。
接下来 e 行,每行两个整数 u,v,表示存在一条连接左部点 u 和右部点 v 的边。
输出格式
输出一行一个整数,代表二分图最大匹配的边数。
#include<bits/stdc++.h> #define N 2005 using namespace std; int n,m,e,ans; int vis[N][N]; int ask[N],matched[N]; bool found(int x) { for (int i = 1 ; i <= m ; i++) if (vis[x][i]) { if (ask[i]) continue; ask[i] = 1; if (!matched[i] || found(matched[i])) { matched[i] = x ; return true; } } return false; } void match() { int cnt = 0; memset(matched,0,sizeof(matched)); for (int i = 1 ; i <= n ; i++) { memset(ask,0,sizeof(ask)); if (found(i)) cnt++; } ans = cnt; } int main(){ scanf("%d%d%d",&n,&m,&e); for (int i = 1 ; i <= e ; i++){ int x,y; scanf("%d%d",&x,&y); vis[x][y] = 1; } match(); printf("%d \n",ans); return 0; }