Zoj 3261 Connections in Galaxy War (逆向并查集)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3261

题目大意:先给你N个城市的能力, 然后给你M条路,再然后又Q次询问,如果为 query,输入 x,则询问与当前城市x相连的且比当前城市能力大的城市中能力最大的城市的编号,如果有多个最大城市,输出编号最小的,否则输出-1.如果为destory,输入x,y,则切断x,y这条路。

解题思路:如果正向查找的话,当切断一条路时没办法确定他们之间的关系了,因为查找时为路径压缩;

但是我们可以把询问存起来,把切断的路径标记一下,没标记的路径进行合并,注意合并的时候把能力大的城市当做根,如果能力一样,位置小的当做根,倒着遍历,如果遇见切断路径,就把它加进路径里面,接着就是并查集了。

做题心得:看到这道题不会做, 网上百度一下, 学习一下, 其实发现这道题没有那么困难, 难度也就是一般的并查集, 加点技巧就可以了,只是没有做过这种类型 的题,想不到方法。 不过看到大牛写的代码只后还是很佩服他们, 用了好多技巧, 代码中的唯一标记方法, 以前只是听说, 没见过,第一次见;还有我感觉特牛的是大牛竟然用map标记变,很方便, 如果想不到了, 最好方法就是vector标记了, 也不是很方便 啊, 所以这道题收货还是不小的、

</pre></p><p>代码如下:</p><p><pre name="code" class="cpp">#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
using namespace std;
const int N = 10005;
const int M = 20005;
const int Q = 50005;
int f[N], ans[Q]; //f数组是并查集数组, ans数组存最后结果
//每个城市
struct node
{
    int Id; //编号
    int power;//能力大小
}p[N]; 
//城市的边
struct link
{
    int x, y;
}q[M];
//存询问消息
struct que
{
    char s[10];
    int x;
    int y;
}d[Q];
//用map标记变
map<int, bool> mp; 
//并查集
int findset(int x) 
{
    return f[x] == x? x : f[x] = findset(f[x]);
}
//合并
void merge(int x, int y) 
{
    int rootx = findset(x);
    int rooty = findset(y);
    if(rootx != rooty)  //根据题意要求进行合并
    {
        if(p[rootx].power > p[rooty].power)
            f[rooty] = rootx;
        else if(p[rootx].power < p[rooty].power)
            f[rootx] = rooty;
        else
        {
            if(p[rootx].Id > p[rooty].Id)
                f[rootx] = rooty;
            else
                f[rooty] = rootx;
        }
    }
}
int main()
{
    int n, m, t, flag = 1;
    while(~scanf("%d", &n))
    {
        mp.clear(); //每次开始清空 
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &p[i].power);
            p[i].Id = i;
            f[i] = i;
        }
        scanf("%d", &m);
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d", &q[i].x, &q[i].y);
            //标记边  交换是因为边无向的(1,2)和(2,1)是同一条边 交换之后就可以使之更容易判断
            if(q[i].x > q[i].y)
               swap(q[i].x, q[i].y);
            mp[q[i].x*10000+q[i].y] = false; //一个标记技巧, 使每条边的标记都是唯一的
        }
        //保存查询结果
        scanf("%d", &t);
        for(int i = 0; i < t; i++)
        {
            scanf("%s", d[i].s);
            if(d[i].s[0] == 'q')
                scanf("%d", &d[i].x);
            else
            {
                scanf("%d%d", &d[i].x, &d[i].y);
                if(d[i].x > d[i].y)
                   swap(d[i].x, d[i].y);
                mp[d[i].x*10000+d[i].y] = true;//标记被毁坏的边
            }
        }
        //把没有毁坏的边先合并起来
        for(int i = 0; i < m; i++)
            if(!mp[q[i].x*10000+q[i].y])
                merge(q[i].x, q[i].y);
        //逆向查找
        int cnt = 0;
        for(int i = t-1; i >= 0; i--)
            if(d[i].s[0] == 'q')
            {
                int k = findset(d[i].x);
                if(p[k].power > p[d[i].x].power)
                    ans[cnt++] = p[k].Id;
                else
                    ans[cnt++] = -1;
            }
            else//如果边被毁坏,则毁坏之前肯定联通,就加进去
                merge(d[i].x, d[i].y);
        if(flag)
            flag = 0;
        else
            printf("\n");
        for(int i = cnt-1; i >= 0; i--)
            printf("%d\n", ans[i]);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值