链接: http://codeforces.com/contest/1027/problem/F
题意:小明有n场考试,每场考试可以在a或者b天考,但是小明每天只能做一套考试卷子,问你小明能否考完所有的考试如果可以输出最大的考试日,如果不可以,输出-1
思路: 看问题其实是一个最优匹配的问题,如果我们我们把点分为两类,左边的点为考试,右边的点为某天。那么我们要做的就是找到左边点全部匹配并且右边的点最小的点。 注意到这里的每一场考试只有两个考试日,也就是只有两个天数点与他相连,那么我可以把这个考试点缩掉,缩成边,那么就可以转化为为每个边匹配一个与他相邻的点,使得最大的点最小。
这里图就分成了几个联通块,那么对于每个联通块,如果他的节点数等于边数,那么肯定都要选。如果节点数等于边数+1,那么我只需要加次小的节点。 如果边数> 点数,肯定是无解呀。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int ,int > pii;
const int N =2e6+5;
int f[N];
int num[N];
pii a[N];
int tot;
int vis[N];
int vise[N];
int totn,totm;
int mx,ci;
int n;
vector<pii> ve[N];
void init()
{
tot=0;
for(int i=0;i<N;i++) f[i]=i;
}
int getf(int x)
{
return f[x]==x?x:(f[x]=getf(f[x]));
}
void merge(int x,int y)
{
int t1=getf(x);
int t2=getf(y);
if(t1>t2) swap(t1,t2);
if(t1!=t2){
f[t2]=t1;
}
}
void dfs(int u)
{
if(vis[u]) return ;
vis[u]=1;
if(u>mx){
ci=mx;
mx=u;
}
else if(u>ci){
ci=u;
}
totn++;
for(int i=0;i<ve[u].size();i++){
int v=ve[u][i].first;
int id=ve[u][i].second;
if(vise[id]==0){
vise[id]=1;
totm++;
}
if(!vis[v]){
dfs(v);
}
}
}
int main()
{
init();
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d %d",&a[i].first, &a[i].second);
num[++tot]=a[i].first; num[++tot]=a[i].second;
}
sort(num+1,num+tot+1);
tot=unique(num+1,num+tot+1)-(num+1);
for(int i=1;i<=n;i++){
a[i].first=lower_bound(num+1,num+tot+1,a[i].first)-num;
a[i].second=lower_bound(num+1,num+tot+1,a[i].second)-num;
}
for(int i=1;i<=n;i++){
merge(a[i].first,a[i].second);
ve[a[i].first].push_back(pii(a[i].second,i));
ve[a[i].second].push_back(pii(a[i].first,i));
}
///for(int i=1;i<=n;i++) cout<<a[i].first<<" *** "<<a[i].second<<endl;
int Ans=0;
for(int i=1;i<=tot;i++){
int fa=getf(i);
if(i==fa){
//cout<<"fa "<<fa<<endl;
totn=0; totm=0;
mx=0;
ci=0;
dfs(i);
if(totn<totm){
cout<<-1<<endl;
return 0;
}
if(totn==totm){
Ans=max(Ans,mx);
}
else if(totm+1==totn){
Ans=max(Ans,ci);
}
}
}
cout<<num[Ans]<<endl;
return 0;
}
/*
3
23 27
22 26
27 30
*/