您的位置:首页 > 其它

POJ1703 解题报告

2017-07-22 00:13 471 查看
Find them, Catch them

Time Limit: 1000MSMemory Limit: 10000K
Total Submissions: 46177Accepted: 14210
Description
The police office in Tadu City decides to say ends to the chaos, as launch actions to root up the TWO gangs in the city, Gang Dragon and Gang Snake. However, the police first needs to identify which
gang a criminal belongs to. The present question is, given two criminals; do they belong to a same clan? You must give your judgment based on incomplete information. (Since the gangsters are always acting secretly.)

Assume N (N <= 10^5) criminals are currently in Tadu City, numbered from 1 to N. And of course, at least one of them belongs to Gang Dragon, and the same for Gang Snake. You will be given M (M <= 10^5) messages in sequence, which are in the following two kinds:

1. D [a] [b]

where [a] and [b] are the numbers of two criminals, and they belong to different gangs.

2. A [a] [b]

where [a] and [b] are the numbers of two criminals. This requires you to decide whether a and b belong to a same gang.

Input
The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. Then T cases follow. Each test case begins with a line with two integers N and M, followed by M lines
each containing one message as described above.
Output
For each message "A [a] [b]" in each case, your program should give the judgment based on the information got before. The answers might be one of "In the same gang.", "In different gangs." and "Not
sure yet."
Sample Input
1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4

Sample Output
Not sure yet.
In different gangs.
In the same gang.

Source
POJ Monthly--2004.07.18



这一题的解法为并查集
其实这一题是POJ1182的简化版,之前看书(挑战程序设计竞赛)的时候没看懂1182这个例题,就跳过去了,没想到这里用上了这一题的方法,就因为之前没好好钻研例题,现在多浪费了几个小时,后悔
明天我再写一下POJ1182的题解,今天先搞定这一题

先讲一下我之前的错误的解法,不想看的话就直接拉到下面去看我正确的解法吧
前面那个MLE的我就不说了,我也不知道我怎么就超出内存大小了,我记得我明明是清空了所有的set集合的呀。。
我之后莫名其妙不知道改了哪里它就变成TLE了

下面我说说我开始的两个TLE的解法的思路,这一思路不管用cin/cout还是scanf/printf都TLE
我使用-1来表示第一个组织,-2来表示第二个组织
我定义了一个set对象的数组set<int>enemy[maxn],enemy[i]中的内容是与 i 属于不同组织的人的序号
对于命令D
如果是第一次使用命令D,那么就将par[a]设置为-1,par[b]设置为-2,代表他们分别属于两个组织
如果不是第一次使用命令D,分为两种情况
        1、如果两个人所属的组织都不知道,那么就将a放入enemy[b]中,b放入enemy[a]中,代表两个人所属的组织不
              同
        2、如果其中一个人的组织(比如a的)确定了,那么就将另外一个人(b)放入到相反的组织中,同时enemy[b]
             中的人的组织也就确定了。在进一步的,对于enemy[b]中的人 i ,enemy[i]中的人的的组织也确定了,以此类
             推下去,这一片的人的组织就都确定了
对于命令A
如果两个人a,b满足par[a]!=a且par[b]!=b,就说明两人的组织都确定了,那么就有两种情况
       1、par[a]==par[b] 说明两人在同一组织
       2、par[a]!=par[b]说明两人属于不同组织
如果其中一人的组织确定,另外一人的组织不确定,那么显然两人的组织关系是不确定的
如果两个人的所属的组织都不知道,那么就要在其中一个人的enemy中寻找,如果能找到对方,说明两人属于不同组
织;反之,则属于同一组织
下面是实现代码

#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
const int maxn=1e5+10;
const int maxm=1e5+10;
set<int> enemy[maxn];
bool is_first_used_d;
int par[maxn];
int N,M;

void init();
void set_gang(int man,int gang);
void order_a(int man1,int man2);
void order_d(int man1,int man2);

int main()
{
int t;
cin>>t;
while(t--)
{
cin>>N>>M;
init();
is_first_used_d=true;
for(int i=0;i<=N;i++)
enemy[i].clear();
for(int i=0;i<M;i++)
{
char order;
int man1,man2;
cin>>order>>man1>>man2;
if(order=='A') order_a(man1,man2);
else order_d(man1,man2);
}
}
return 0;
}

void init()
{
for(int i=1;i<=N;i++)
par[i]=i;
}

void set_gang(int man,int gang)
{
par[man]=gang;
int next_gang=gang==-1?-2:-1;
set<int>::iterator ite;
for(ite=enemy[man].begin();ite!=enemy[man].end();ite++)
if(par[*ite]==*ite) set_gang(*ite,next_gang);
enemy[man].clear();
}

void order_a(int man1,int man2)
{
if(par[man1]!=man1&&par[man2]!=man2)
{
if(par[man1]==par[man2])
cout<<"In the same gang.\n";
else
cout<<"In different gangs.\n";
}
else if(par[man1]!=man1&&par[man2]!=man2)
cout<<"Not sure yet.\n";
else
{
set<int>::iterator ite;
bool man2_is_enemy=false;
for(ite=enemy[man1].begin();ite!=enemy[man1].end();ite++)
if(*ite==man2)
man2_is_enemy=true;
if(man2_is_enemy) cout<<"In different gangs.\n";
else cout<<"Not sure yet.\n";
}
}

void order_d(int man1,int man2)
{
if(is_first_used_d)
{
par[man1]=-1;
par[man2]=-2;
is_first_used_d=false;
}
else if(par[man1]==man1&&par[man2]==man2)
{
enemy[man1].insert(man2);
enemy[man2].insert(man1);
}
else if(par[man1]!=man1)
{
if(par[man1]==-1) set_gang(man2,-2);
else set_gang(man2,-1);
}
else if(par[man2]!=man2)
{
if(par[man2]==-1) set_gang(man1,-2);
else set_gang(man1,-1);
}
}


好了,现在开始讲正确的解法
对于每个人 i 创建两个元素,i-first和i-second,并且使用这2*N个元素建立并查集,这个并查集维护如下信息
       1、i-x表示“i属于组织x”
       2、并查集里的每一个组表示组内所有元素代表的情况都同时发生或不发生
例如,如果x-first和y-second在同一个组里,说明x和y属于不同的组织,如果x-first和y-first或者x-second和y-seconf
在同一个组里,那么说明他们属于同一个组织
那么对于命令D,我们只需要进行如下的操作
合并x-first,y-second;合并x-second,y-first
在这里,我们使用x表示x-first,x+N表示x-second
下面是实现代码,注意使用的是scanf和printf,使用cin/cout的话会TLE的

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn=1e5+100;
const int maxm=1e5+100;
int par[2*maxn];
int rnk[2*maxn];
int N,M;

void init(int n);
void unite(int x,int y);
int fin(int x);
bool same(int x,int y);
void order_a(int a,int b);
void order_d(int a,int b);

int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&N,&M);
init(2*N);
while(M--)
{
char order;
int a,b;
getchar();
scanf("%c%d%d",&order,&a,&b);
if(order=='A') order_a(a,b);
else order_d(a,b);
}
}
return 0;
}

void order_a(int a,int b)
{
if(same(a,b+N)) printf("In different gangs.\n");
else if(same(a,b)) printf("In the same gang.\n");
else printf("Not sure yet.\n");
}

void order_d(int a,int b)
{
unite(a,b+N);
unite(a+N,b);
}

void init(int n)
{
for(int i=1;i<=n;i++)
{
par[i]=i;
rnk[i]=0;
}
}

void unite(int x,int y)
{
x=fin(x);
y=fin(y);
if(x==y) return ;
if(rnk[x]<rnk[y]) par[x]=y;
else
{
par[y]=x;
if(rnk[x]==rnk[y]) rnk[x]++;
}
}

int fin(int x)
{
if(par[x]==x) return x;
else return par[x]=fin(par[x]);
}

bool same(int x,int y)
{
return fin(x)==fin(y);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: