zoukankan      html  css  js  c++  java
  • BZOJ1854: [Scoi2010]游戏

    BZOJ1854: [Scoi2010]游戏

    Description

    lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。 游戏进行到最后,lxhgww遇到了终极boss,这个终极boss很奇怪,攻击他的装备所使用的属性值必须从1开始连续递增地攻击,才能对boss产生伤害。也就是说一开始的时候,lxhgww只能使用某个属性值为1的装备攻击boss,然后只能使用某个属性值为2的装备攻击boss,然后只能使用某个属性值为3的装备攻击boss……以此类推。 现在lxhgww想知道他最多能连续攻击boss多少次?

    Input

    输入的第一行是一个整数N,表示lxhgww拥有N种装备 接下来N行,是对这N种装备的描述,每行2个数字,表示第i种装备的2个属性值

    Output

    输出一行,包括1个数字,表示lxhgww最多能连续攻击的次数。

    Sample Input

    3
    1 2
    3 2
    4 5

    Sample Output

    2

    HINT

    【数据范围】
    对于30%的数据,保证N < =1000
    对于100%的数据,保证N < =1000000

    题解Here!

    看到 n<=100w 这个数据范围,瞬间被吓懵了。。。

    然后读了遍题才发现:这不是二分图匹配嘛?

    对于物品 i 的属性 x,y ,分别从 x 和 y 向 i 连一条有向边。将伤害值当做左部点,装备编号当做右部点,求最大匹配即可。

    于是火速一发匈牙利,然而TLE了。为什么?

    因为每次都要将 vis 数组清为 false ,vis数组最小1w,当然TLE。。。

    怎么办呢?

    我们可以将vis数组从 bool 转成 int ,最初清为0,再开一个 time ,每次+1。

    然后每次判断 vis(int)==time 即可做到 vis(bool)==true 的效果。

    证明?不就是每次判断 vis 是否被修改嘛。。。

    注:

    1. 存图用前向星

    2. 若当前伤害值无法匹配,直接跳出。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #define MAXN 1000010
    using namespace std;
    int n,c=1,id=0,f[MAXN],head[MAXN],vis[MAXN];
    struct node{
    	int next,to;
    }a[MAXN<<1];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    inline void add(int x,int y){
    	a[c].to=y;a[c].next=head[x];head[x]=c++;
    }
    bool find(int x){
    	for(int i=head[x];i;i=a[i].next){
    		int v=a[i].to;
    		if(vis[v]!=id){
    			vis[v]=id;
    			if(f[v]==-1||find(f[v])){
    				f[v]=x;
    				return true;
    			}
    		}
    	}
    	return false;
    }
    void work(){
    	int ans=0;
    	for(int i=1;i<=10000;i++){
    		id++;
    		if(find(i))ans++;
    		else break;
    	}
    	printf("%d
    ",ans);
    }
    void init(){
    	int x,y;
    	n=read();
    	memset(f,-1,sizeof(f));
    	for(int i=1;i<=n;i++){
    		x=read();y=read();
    		add(x,i);add(y,i);
    	}
    }
    int main(){
    	init();
    	work();
    	return 0;
    }
    

    然而,此题还有更厉害的算法——并查集!

    我第一次看到网上 dalao 的并查集题解时,我惊呆了。。。

    把一个有a,b两种属性的武器看成点a,b之间的无向边。

    对于一个联通块,假如不含环(就是一棵树),那么必定可以满足其中任意的p-1个点。

    对于一个联通块,假如含环,那么必定全部的p个点都能满足。

    那么合并并查集的时候可以利用一个 vis 来维护这个性质。

    把权值看成点,把武器看成边。

    如果每次加入的边是合并两个联通块,就把权值小的联通块并到权值大的联通块,然后给权值小的 vis=true 

    如果不是,就把该联通块的顶点的 vis=true 。

    这样就可以保证,如果一个大小为N联通块:

    1. 由N-1条边构成,最大点的 vis=false ,其他为 true 。

    2. 由≥N条边构成,所有点的 vis=true 。

    然后最后只要一次扫描vis就可以得出答案了。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define MAXN 1000010
    using namespace std;
    int n,fa[MAXN];
    bool vis[MAXN];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    inline void uniun(int x,int y){
    	x=find(x);y=find(y);
    	if(x==y)vis[x]=true;
    	else{
    		if(x<y)swap(x,y);
    		if(vis[y])vis[x]=true;
    		else vis[y]=true;
    		fa[y]=x;
    	}
    }
    int main(){
    	int x,y;
    	n=read();
    	for(int i=1;i<=n+1;i++){fa[i]=i;vis[i]=false;}
    	for(int i=1;i<=n;i++){
    		x=read();y=read();
    		uniun(x,y);
    	}
    	for(int i=1;i<=10001;i++)if(!vis[i]){printf("%d
    ",i-1);break;}
    	return 0;
    }
    
  • 相关阅读:
    mysq 日期相减
    说说时间观与时间管理——北漂18年(71)
    ionic之切换开关
    ionic之单选框
    SELECT ... LOCK IN SHARE MODE和SELECT ... FOR UPDATE locks在RR模式下可以看到最新的记录
    14.5.2.3 Consistent Nonlocking Reads 一致性非锁定读
    14.5.2.2 autocommit, Commit, and Rollback
    14.5.2 事务隔离级别
    对于唯一索引使用唯一条件搜索, InnoDB 只锁定找到的index record,不是它之前的区间
    mysql explain 解释
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/8977731.html
Copyright © 2011-2022 走看看