2014携程第一题总结

灵感来自2014携程赛的第一题。
 这个题目的关键在于如何建立合适的模型对三类状态进行刻画。
这道题目巧妙地在并查集中运用了带权向量来表示(推算)同根元素之间的关系。把并查集中的元素属性从原先的属于根拓展到属于根和相互关系。
建模思想:因为总共有三类状态,相互关系有三种,用rank【k】表示k与其最祖先节点的关系,0表示同类,1表示自己能吃最祖先,2表示最祖先吃自己。用rank【k】表示向量f(x,father【x】),(d - 1)可以表示f(x,y),用向量来表示互相关系~!
先看向量建模,基本图:
 
(由B -> A 和 A -> C 的关系可以推出 B -> C的关系:B被A吃,A被C吃,或者说:C吃A,A吃B,所以B吃C,符合三角关系 )

 为什么可行呢?首先因为合适的建模方式和向量的可加性,其次因为只有3种关系的状态,所以运用%3可以表达这样的关系。
 

如图,在可加性的基础上用%3即可达到神奇的效果。其实仔细想想:由向量的可加性,我们按照题目给出的数据对各个节点的rank值
进行增加,如果只是单纯的增加,最后会形成一个分优先前后线性表,而现在对数据%3,就是将线性表变成环装表,或者更准确的说:
变成三角表(因为只有三种状态)。 

 
所以现在我们就可以理解Judge中
if(((d - 1 + rank[y]) % 3) == rank[x])这句话的意思了。
 

如果向量f(x,y)+ f(x,fa【x】) == f(y,fa【y】) (x,y同根,所以fa【x】== fa【y】)
则说明这句话是真话,否则是假话。

 
在find函数中,有回溯更新儿子节点的过程
 

fa[y] -> fa[x]的为union中干的事情,它只把fa[y]牵了过去,而黑色箭头为后来find中更新儿子节点时的操作(rank1先,rank2后)

下面为代码(同p1182) 
 
#include <stdio.h>
#define MAXN 50000
int fa[MAXN + 5],rank[MAXN + 5];
int n,k,d,x,y,count;
void Init(){
    for(int i = 1;i <= n;i ++){
        fa[i] = i;
        rank[i] = 0;
    }
    return;
}
int find(int x){
    if(x == fa[x]) return x;
    //如果第一步判断是错误,那么说明x是某个节点的儿子,需要更新儿子
    //下面是回溯更新儿子
    int prefa = fa[x];
    fa[x] = find(fa[x]);//直到找到最祖先节,找到以后更新最祖先与儿子们的关系
    rank[x] = (rank[x] + rank[prefa]) % 3;
    return fa[x];//返回给底下一层的是所找到的最祖先节点
}
bool Judge(int x,int y,int d){
    if(x > n || y > n || (x == y) && (d == 2))
        return true;//假话
    int fx = find(x),fy = find(y);
    if(fx == fy){//同根,判断描述是否为真if(((d - 1 + rank[y]) % 3) == rank[x])
            return false;//真话
        else
            return true;//假话
    }
    else
        return false;//异根,需要先合并
}
void Union(int x,int y,int d){
    int fx = find(x),fy = find(y);
    if(fx == fy)
        return;
    fa[fx] = fy;
    rank[fx] = (d - 1 + rank[y] - rank[x] + 3) % 3;
    //出现了减号,要+3防止出现负数
    return;
}
int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d %d",&n,&k);
    Init();
    count = 0;
    for(int i = 1;i <= k;i ++){
        scanf("%d %d %d",&d,&x,&y);
        if( Judge(x,y,d) ){//判断是否假话或是否同根
            count ++;
        }
        else{
            Union(x,y,d);
        }
    }
    printf("%d\n",count);
    return 0;
}
 
剪刀石头布
Problem Description
现有M个人一起玩剪刀石头布,以1-M编号,每人出一种,出过不再改变,但是我们并不知道它到底是哪一种。 (其中石头赢剪刀,剪刀赢布,布赢石头,一样则平)
裁判用两种说法对这M个人所构成的输赢关系进行描述:
一:"1 A B",表示第A个人和第B个人出的一样。
二:"2 A B",表示第A个人赢第B个人。
裁判对M个人,用以上两种说法,连说N句话,其中有真的、也有假的。
一句话出现以下情况,就是假话,否则就是真话。
1) 该句话与之前的某些真话冲突;
2) 该句话中A或B比M大;
3) 该句话表示A赢A。

请根据给定的M和N,输出假话数。
其中(1 <= M <= 10,000),(0 <= N <= 10,000)
 
Input
第1行是一个自然数K,代表有K组数据。
每组数据以一个空行分隔,其中每组数据的第1行是两个自然数M、N,以空格分开。
每组数据的第2行至N+1行,每行是三个自然数X,A,B,三个数之间用空格分开,X(1或2)表示说法的种类。
 
Output
每组数据对应一行,每行有一个整数,代表假话数。
 
Sample Input
3
 
43 11
1 4 3
2 3 3
1 4 1
1 4 4
2 3 3
1 2 2
2 1 4
1 1 1
2 1 4
2 3 4
2 3 2
 
66 9
2 3 1
2 4 4
2 1 2
2 4 3
2 4 2
2 2 3
1 3 2
1 2 1
1 1 1
 
6 7
2 3 7
2 1 2
2 4 4
1 2 1
1 3 2
1 2 3
2 1 3
 
Sample Output
5
4
3
 
 
 

 

posted @ 2014-05-29 17:39  Naturain  阅读(141)  评论(0编辑  收藏  举报