1703:发现它,抓住它

网址如下:

OpenJudge - 1703:发现它,抓住它

测,这题差点让我去世

用了一堆方法来做

后面现学了并查集,用了并查集来做,因为缩短路径的方法不好,还是超时了

后面换了一种缩短路径的方法

先上代码

解法一(TLE)

#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;

const int maxn = 100001;

int kase[maxn], N, M;
vector<int> G[maxn];
bool vis[maxn];

void init(void){
    for(int i = 1; i <= N; i++) G[i].clear();
    memset(kase, -1, sizeof(kase));
}
void chg(int idx){
    vis[idx] = true;
    for(int i = 0; i < G[idx].size(); i++) if(!vis[G[idx][i]]) chg(G[idx][i]);
    kase[idx] = !kase[idx];
}
void fill(int idx, int b){
    vis[idx] = true;
    if(vis[b]) return;
    for(int i = 0; i < G[idx].size(); i++) if(!vis[G[idx][i]]) fill(G[idx][i], b);
}
void distinguish(int a, int b){
    if(kase[a] == -1 && kase[b] == -1){kase[a] = 0; kase[b] = 1;}
    else if(kase[a] == -1){kase[a] = !kase[b];}
    else if(kase[b] == -1){kase[b] = !kase[a];}
    else if(kase[a] == kase[b]){memset(vis, 0, sizeof(vis)); chg(a);}
    G[a].push_back(b); G[b].push_back(a);
}
void ans(int a, int b){
    if(kase[a] == -1 || kase[b] == -1) printf("Not sure yet.\n");
    else{
        memset(vis, 0, sizeof(vis));
        fill(a, b);
        if(vis[b]){
            if(kase[a] == kase[b]) printf("In the same gang.\n");
            else printf("In different gangs.\n");
        }
        else printf("Not sure yet.\n");
    }
}

int main(void)
{
    int T; scanf("%d", &T);
    while(T--){
        scanf("%d%d", &N, &M);
        init();
        while(M--){
            getchar(); char c = getchar();
            int a, b; scanf("%d%d", &a, &b);
            if(c == 'A') ans(a, b);
            else distinguish(a, b);
        }
    }
    
    return 0;
}

思路

我就是根据已有的情报直接假设哪几个案件是哪个团伙干的

只需要维护这几个案件的关系满足情报就行

但是因为是随意假设的,所以会出现假设完的两个案件与情报不符

这就要改变其中一个案件(0变1或1变0)

或者说“案件图”

因为D a b把这一片案件连起来了

所以都要改

同时,还有一种意外情况,两个案件不在同一个“案件图”上,案件都不为-1(代表未定义)

就需要事先判断二者在同一个“案件图”上


总结
我们需要做的:记录“情报图”,并维护案件的关系与情报相符
两个案件可以判断关系的前提条件:都定义了,且在同一个“情报图”上

但是TLE了

解法二(TLE)

#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 100001;

int kase[maxn], N, M;
int color[maxn];
int newcolor;

void init(void){
    memset(kase, -1, sizeof(kase));
    memset(color, 0, sizeof(color));
    newcolor = 1;
}
void paint(int Dst, int Src){
    for(int i = 1; i <= N; i++) if(color[i] == Src) color[i] = Dst;
}
void distinguish(int a, int b){
    if(kase[a] == -1 && kase[b] == -1){kase[a] = 0; kase[b] = 1; color[a] = color[b] = newcolor++;}
    else if(kase[a] == -1){kase[a] = !kase[b]; color[a] = color[b];}
    else if(kase[b] == -1){kase[b] = !kase[a]; color[b] = color[a];}
    else if(kase[a] == kase[b]){paint(color[a], color[b]);}
    
}
void ans(int a, int b){
    if(kase[a] == -1 || kase[b] == -1) printf("Not sure yet.\n");
    else{
        if(color[a] == color[b]){
            if(kase[a] == kase[b]) printf("In the same gang.\n");
            else printf("In different gangs.\n");
        }
        else printf("Not sure yet.\n");
    }
}

int main(void)
{
    int T; scanf("%d", &T);
    while(T--){
        scanf("%d%d", &N, &M);
        init();
        while(M--){
            getchar(); char c = getchar();
            int a, b; scanf("%d%d", &a, &b);
            if(c == 'A') ans(a, b);
            else distinguish(a, b);
        }
    }
    
    return 0;
}

思路

根据上面我所说的,我只是把一个“案件图”涂上了相同的颜色,来标记这些案件属于同一个“案件图”。这样,每次出现两个案件不在同一个“案件图”上,案件都不为-1(代表未定义)的情况,合并“案件图”就只需要把一个“案件图”的颜色涂成另一个“案件图”的

就不需要解法一的vector来记录图的结点之间的联系,减少了内存操作

但是每次重涂就需要遍历所有的案件

重涂数量多了就完蛋

最后还是TLE

解法三(TLE)

#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 100001;

int kase[maxn], N, M;
int color[maxn];
int head[maxn], next[maxn];
int newcolor;

void init(void){
    memset(kase, -1, sizeof(kase));
    memset(color, 0, sizeof(color));
    memset(head, 0, sizeof(head));
    memset(next, 0, sizeof(next));
    newcolor = 1;
}
inline void list_insert(int idx, int color){
    next[idx] = head[color];
    head[color] = idx;
}
void paint(int Dst, int Src){
    for(int i = head[Src]; i; i = next[i]){color[i] = Dst; kase[i] = !kase[i]; list_insert(i, Dst);}
}
void distinguish(int a, int b){
    if(kase[a] == -1 && kase[b] == -1){kase[a] = 0; kase[b] = 1; color[a] = color[b] = newcolor; list_insert(a, newcolor); list_insert(b, newcolor++);}
    else if(kase[a] == -1){kase[a] = !kase[b]; color[a] = color[b]; list_insert(a, color[b]);}
    else if(kase[b] == -1){kase[b] = !kase[a]; color[b] = color[a]; list_insert(b, color[a]);}
    else if(kase[a] == kase[b]){paint(color[a], color[b]);}
    
}
void ans(int a, int b){
    if(kase[a] == -1 || kase[b] == -1) printf("Not sure yet.\n");
    else{
        if(color[a] == color[b]){
            if(kase[a] == kase[b]) printf("In the same gang.\n");
            else printf("In different gangs.\n");
        }
        else printf("Not sure yet.\n");
    }
}

int main(void)
{
    int T; scanf("%d", &T);
    while(T--){
        scanf("%d%d", &N, &M);
        init();
        while(M--){
            getchar(); char c = getchar();
            int a, b; scanf("%d%d", &a, &b);
            if(c == 'A') ans(a, b);
            else distinguish(a, b);
        }
    }
    
    return 0;
}

思路

那优化就很简单了,既然每次重涂都需要遍历所有元素,那么我建立链表,将需要改变的案件记录起来就行了

但是还是TLE,因为数据太大了

解法四(TLE)

#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 100001;

int condition[maxn], N, M;
int parent[maxn], rank[maxn];
int newcolor;

void init(void){
    memset(condition, 0, sizeof(condition));
    memset(rank, 1, sizeof(rank));
    for(int i = 1; i <= N; i++) parent[i] = i;
    newcolor = 1;
}
int find(int idx){
    if(parent[idx] == idx) return idx;
    return find(parent[idx]);
}
int get_relation(int idx){
    if(parent[idx] == idx) return 0;
    return get_relation(parent[idx]) ^ condition[idx];
}
void join(int idx1, int idx2){
    int prt1 = find(idx1), prt2 = find(idx2);
    if(prt1 == prt2) return;
    int r1 = get_relation(idx1), r2 = get_relation(idx2);
    if(rank[prt1] < rank[prt2]){parent[prt1] = prt2; condition[prt1] = !(r1 ^ r2);}
    else{
        if(rank[prt1] == rank[prt2]) rank[prt2]++;
        parent[prt2] = prt1; condition[prt2] = !(r1 ^ r2);
    }
}
void ans(int a, int b){
    int prt1 = find(a), prt2 = find(b);
    if(prt1 != prt2) printf("Not sure yet.\n");
    else{
        int r1 = get_relation(a), r2 = get_relation(b);
        if(r1 == r2) printf("In the same gang.\n");
        else printf("In different gangs\n");
    }
}

int main(void)
{
    int T; scanf("%d", &T);
    while(T--){
        scanf("%d%d", &N, &M);
        init();
        while(M--){
            getchar(); char c = getchar();
            int a, b; scanf("%d%d", &a, &b);
            if(c == 'A') ans(a, b);
            else join(a, b);
        }
    }
    
    return 0;
}

思路

考虑到上面的图的特点:不相交,并且需要合并,还要查找元素的关系

然后我去学了并查集

可以看看这篇博客:【算法与数据结构】—— 并查集-CSDN博客

讲的非常简单易懂

然后我用了并查集,并使用了上面那篇博客里面讲的第二种的缩短路径的方法

其中condition记录案件和父案件的关系

parent和rank的意义见上面那篇博客

然后

还是TLE

我差点裂了

解法五(AC)

#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 100001;

int condition[maxn], N, M;
int parent[maxn];
int newcolor;

void init(void){
    memset(condition, 0, sizeof(condition));
    for(int i = 1; i <= N; i++) parent[i] = i;
}
int find(int idx){
    if(parent[idx] == idx) return idx;
    int par = parent[idx];
    while(parent[par] != par){
        condition[idx] = condition[idx] ^ condition[par];
        parent[idx] = parent[par];
        par = parent[idx];
    }
    return find(parent[idx]);
}
void join(int idx1, int idx2){
    int prt1 = find(idx1), prt2 = find(idx2);
    if(prt1 == prt2) return;
    parent[prt1] = prt2;
    condition[prt1] = !(condition[idx1] ^ condition[idx2]);
}
void ans(int a, int b){
    int prt1 = find(a), prt2 = find(b);
    if(prt1 != prt2) printf("Not sure yet.\n");
    else{
        if(condition[a] == condition[b]) printf("In the same gang.\n");
        else printf("In different gangs.\n");
    }
}

int main(void)
{
    int T; scanf("%d", &T);
    while(T--){
        scanf("%d%d", &N, &M);
        init();
        while(M--){
            getchar(); char c = getchar();
            int a, b; scanf("%d%d", &a, &b);
            if(c == 'A') ans(a, b);
            else join(a, b);
        }
    }
    
    return 0;
}

思路

最后就是这玩意了

用到了上述博客提到的缩短路径的第一种方法

condition的意义和解法四的一样

最后AC了

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值