两道“2选择”问题

11 篇文章 0 订阅
6 篇文章 0 订阅

题目链接:

题目1https://ac.nowcoder.com/acm/contest/5673/I
题目2http://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();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值