题目链接:
题目1:https://ac.nowcoder.com/acm/contest/5673/I
题目2:http://acm.hdu.edu.cn/showproblem.php?pid=6808
基本模型很相似:
有n个选择,第i个选择有ai、bi可选,每个选择最多选一个。
题目1:当前没出现过的ai、bi都可以选,每次可不选或者选一个。问可以选的最大个数。
题目2:当前ai、bi都可以选,每次只能且必须选一个。求最小的选择的不同数字的个数。
解法:
在将ai,bi看成点,且ai、bi连边的基础上进行。
设有N个点,V条边。
题目1:往图靠拢,将建成若干个连通块。
会发现,如果如果某连通块是一棵树,其中点数为ni,那么ni-1就是答案。若该图有环,那么ni为答案。于是问题转换成询问每个连通块是否是树【判断一下边和点的数量关系即可】【查并集】
题目2:对于每组 t 和 x ,可以计算t==0时可能的位置,于是每一组有两个答案可选:x-t 与 x+t 。同往图靠拢,于是转换成最小点覆盖问题。最小点覆盖即为二分图最大匹配数,KM算法复杂度是O(NV)会T,所以采用dinic最大流O(N0.5V)。
AC代码:
题目1:
#include <iostream>
#include <algorithm>
#include<cmath>
#include<stack>
#include<queue>
#include<unordered_map>
#include<vector>
#define ll long long
#define pii pair<int,int>
using namespace std;
const int maxn=1e6+6;
const int mod=1e9+7;
unordered_map<int,int>mp;
int siz[maxn],edg[maxn],fa[maxn];
int tot;
int ffa(int x){
if(fa[x]==x)return x;
return fa[x]=ffa(fa[x]);
}
void unin(int x,int y){
x=ffa(x);y=ffa(y);
if(x!=y){
fa[y]=x;
siz[x]+=siz[y];
edg[x]+=edg[y]+1;
}else{
edg[x]++;
}
}
int A[maxn],B[maxn];
void work(int t){
int n;
scanf("%d",&n);
mp.clear();
tot=1;
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
if(mp[a]==0)mp[a]=tot++;
if(mp[b]==0)mp[b]=tot++;
A[i]=mp[a];
B[i]=mp[b];
fa[A[i]]=A[i];fa[B[i]]=B[i];
siz[A[i]]=1;siz[B[i]]=1;//点
edg[A[i]]=edg[B[i]]=0;//边
}
for(int i=1;i<=n;i++){
unin(A[i],B[i]);
}
int ans=0;
for(int i=1;i<tot;i++){
if(ffa(i)==i){
if(siz[i]-1<edg[i])ans+=siz[i];
else ans+=(siz[i]-1);
}
}
printf("Case #%d: %d\n",t,ans);
}
int main(){
int T,t;
cin>>T;
t=1;
while(T--){
work(t);
t++;
}
}
题目2:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<sstream>
#define ll long long
#define pii pair<int,int>
using namespace std;
const int maxn=6e5+6;
const int inf=0x3f3f3f3f;
int n,m;
int head[maxn],nx[maxn],ver[maxn],C[maxn];
int tot;
void add(int u,int v,int c){//
ver[tot]=v;
nx[tot]=head[u];
C[tot]=c;
head[u]=tot++;
}
int dep[maxn];
bool bfs(int s,int t){
queue<int>qu;
while(!qu.empty())qu.pop();
memset(dep,0,sizeof(dep));
dep[s]=1;
qu.push(s);
while(!qu.empty()){
int x=qu.front();qu.pop();
for(int i=head[x];i!=-1;i=nx[i]){
if(C[i]){
int v=ver[i];
if(!dep[v]){
dep[v]=dep[x]+1;
qu.push(v);
}
}
}
}
if(dep[t]==0)return 0;
return 1;
}
int dinic(int s,int t,int u,int flow){
if(u==t||flow==0)return flow;
int res=flow;
for(int i=head[u];i!=-1;i=nx[i]){
if(C[i]){
int v=ver[i];
if(dep[v]==dep[u]+1){
int k=dinic(s,t,v,min(res,C[i]));
C[i]-=k;
C[i^1]+=k;
res-=k;
if(res==0)return flow;
}
}
}
return flow-res;
}
void init(){
memset(head,-1,sizeof(head));
}
int t[maxn],x[maxn],xx[maxn];
int x1[maxn],x2[maxn];
map<pii,int>mp;
void work(){
int n;
scanf("%d",&n);
int cnt=0;
mp.clear();
for(int i=1;i<=n;i++){
scanf("%d%d",&t[i],&x[i]);
int xx1=x[i]-t[i];
int xx2=x[i]+t[i];
xx[cnt++]=xx1;
xx[cnt++]=xx2;
}
sort(xx,xx+cnt);
cnt=unique(xx,xx+cnt)-(xx);
int S,T;
S=cnt+1;T=cnt+2;
init();
tot=0;
for(int i=1;i<=n;i++){
x1[i]=lower_bound(xx,xx+cnt,x[i]-t[i])-(xx)+1;
x2[i]=lower_bound(xx,xx+cnt,x[i]+t[i])-(xx)+1;
if(mp[pii(S,x1[i])]==0){
add(S,x1[i],1);
add(x1[i],S,0);
mp[pii(S,x1[i])]=1;
}
if(mp[pii(x2[i]+cnt,T)]==0){
add(x2[i]+cnt,T,1);
add(T,x2[i]+cnt,0);
mp[pii(x2[i]+cnt,T)]=1;
}
if(mp[pii(x1[i],x2[i]+cnt)]==0){
add(x1[i],x2[i]+cnt,1);
add(x2[i]+cnt,x1[i],0);
mp[pii(x1[i],x2[i]+cnt)]=1;
}
}
int ans0=0;
while(bfs(S,T)){
int fl=0;
while((fl=dinic(S,T,S,inf)))ans0=ans0+fl;
}
printf("%d\n",ans0);
}
int main(){
int T;
cin>>T;
while(T--){
work();
}
}