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

来源:互联网 发布:学术论文查重软件 编辑:程序博客网 时间:2024/06/05 15:18

题目链接: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
原创粉丝点击