题目链接Head of a Gang
题目大意:给出一些人的关系,有权重,从中选出集合元素个数大于等于3人且总权重大于k的团队,并且输出团队的队长(与其相连的权重最大)。
参考博客九度oj-head of gang-并查集
思路:本题是传统并查集的变形。大致思路是,先建立二级并查集,最后处理并查集,得出符合条件的团队。
结构体采用名字和权重,建立结构体数组per。建立并查集的过程基本不变,只不过,要计算团队的人数sum,通过合并的时候,加入根节点处的sum。father下标是在输入中出现的顺序,father用于后面得到根节点,建立并查集的过程中,更新per数组。最后根据条件层层筛选出符合条件的团队。
代码如下:
#include
#include
#include
#include
using namespace std;
const int MAX = 2000;
struct person
{
char name[3];//名字
int time;//与其相关的权值之和
};
struct header
{
char name[3];//名字
int members;//团队总人数
bool operator < (const header &b) const//按名字升序排序
{
int tmp = strcmp(name, b.name);
return tmp<0;
}
};
person per[MAX];//人
header head[MAX/2];//团队
int father[MAX];
int sum[MAX];//记录每个集合的个数
int findroot(int x)
{
if(father[x]==x) return x;
int r, t = x;
while(father[t]!=t) t = father[t];
r = t;
while(father[x]!=x)
{
t = father[x];
father[x] = r;
x = t;
}
return r;
}
void join(int a, int b)
{
int fa = findroot(a), fb = findroot(b);
if(fa != fb)
{
father[fa] = fb;
sum[fb] += sum[fa];
}
}
int main()
{
int N, K, i, j;
while(scanf("%d%d", &N, &K)!=EOF)
{
char name1[5];
char name2[5];
char name[15];
int t, num = 0;//num-1为per数组的大小
for(int i=0; i<MAX; i++)
{
father[i] = i;
sum[i] = 1;
}
while (getchar() != '\n');
while(N--)
{
int i = 0, j = 0;
scanf("%s%s%d", name1, name2, &t);
for(i=0; i<num; i++)
{
if(strcmp(name1, per[i].name)==0) break;
}
if(i==num)//数组per中没有
{
strcpy(per[i].name, name1);
per[i].time = t;
num++;
}
else
{
per[i].time += t;//累加权值
}
for(j=0; j<num; j++)
{
if(strcmp(name2, per[j].name)==0) break;
}
if(j==num)//数组per中没有
{
strcpy(per[j].name, name2);
per[j].time = t;
num++;
}
else
{
per[j].time += t;//累加权值
}
join(i, j);
}
//这一步很重要,将同一个团伙的成员的根节点置成一样的,方便后来的查询。
for(int i=0; i<num; i++) findroot(i);
//处理并查集
int index = 0;
int ans = 0;//符合条件的团队个数
for(i=0; i<num; i++)
{
int maxt = 0;//一个团队中的最大权值
int maxind = 0;//一个团队中的头目下标
int sum_talk = 0;//一个团队的总权值
if(father[i]==i && sum[i]>2)//总人数大于2的团队
{
for(j=0; j<num; j++)
{
if(father[j]==i)//该团队成员
{
if(per[j].time>maxt)//寻找团队头目
{
maxt = per[j].time;
maxind = j;
}
sum_talk += per[j].time;
}
}
if(sum_talk/2>K)//二倍
{
strcpy(head[index].name, per[maxind].name);
head[index].members = sum[i];
index++;
ans++;
}
}
}
sort(head, head+index);
printf("%d\n", ans);
if(ans!=0)
{
for(int i=0; i<index; i++)
{
printf("%s %d\n", head[i].name, head[i].members);
}
}
}
return 0;
}
题目链接最短路径
参考博客九度 OJ1100 最短路径(需要使用高精度整数)
代码:
#include
#include
using namespace std;
const int MAX = 101;
int father[MAX];
int d[MAX][MAX];
int N, M, a, b;
int findRoot(int x)
{
if(father[x]==-1) return x;
int root, t = x;
while(father[t]!=-1) t = father[t];
root = t;
while(father[x]!=-1)
{
t = father[x];
father[x] = root;
x = t;
}
return root;
}
void join(int a, int b)
{
int fa = findRoot(a), fb = findRoot(b);
if(fa!=fb) father[fa] = fb;
}
int main()
{
while(cin >> N >> M)
{
int dis = 1;
memset(father, -1, sizeof(father));
memset(d, 0, sizeof(d));
for(int i=0; i<M; i++)//类似于MST的求法,边的权值不断增加
{
cin >> a >> b;
int fa = findRoot(a), fb = findRoot(b);
if(fa==fb)//同一个集合中,a到b的最短路径已求出
{
dis = (2*dis)%100000;
continue;
}
for(int k=0; k<N; k++)//求a和b集合中所有点的最短路径(经过a-b边)
{
if(fa!=findRoot(k)) continue;
for(int j=0; j<N; j++)
{
if(fb!=findRoot(j)) continue;
d[k][j] = d[j][k] = (d[k][a]+dis+d[b][j])%100000;//最短路径长度
}
}
dis = (2*dis)%100000;
join(a, b);
}
for(int i=1; i<N; i++)//输出路径长度
{
if(d[0][i]==0) cout << -1 << endl;
else cout << d[0][i] << endl;
}
}
return 0;
}