hdu 5876

讨论

图论,广搜,确切说是补图的广搜,对于给定的图G,其补图H中,原来G中存在的边在H中都不存在,原来G中没有的边在H中都存在,把G和H拼在一起就是一个完全图,算法很简单,对于一个点,找出与其不直连的点,向这些点广搜,重复这个过程即可
实现层面上需要一点技巧,也只是一点,当已经处理过所有N个点之后就可以弹出了,这样虽然复杂度没有改进(每个点都要扫一遍与其直连的边),但是对于边数较少的图可以非常快的完成,比如200000个点,0个边,由于第一次就完成了所有点的处理,直接弹出就可以了,否则需要对所有点都进行一次广搜,会非常慢的
比赛的时候竟然看了半天看不懂题意,未免有点太神经过敏了,而后来打扫的时候由于邻接表开小了一半导致一排TLE(只有尝试写入越界才会RE),如果是java的话恐怕第一次RE马上就能反应到
为了能稍微快一点(实际上快了100+ms),对memset和输出进行了一点优化

题解状态

546MS,4576K,1496 B,C++

题解代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 200004
#define MAXM 40004
#define memset0(a) memset(a,0,sizeof(a))

int N, M, S;//点数 原图的边数 起点
int to[MAXM], pre[MAXM], al[MAXN], index, dis[MAXN];//前四个是邻接表的 分别是边指向的点 上一条边的下标 每个起点最后插入的边的下标 分配下表的变量 dis则是最短距离
bool used[MAXN], mk[MAXN];//点已经使用过 存在直连的边
void fun()
{
    index = 1;
    for (int p = 0; p < M; p++) {
        int a, b;
        scanf("%d%d", &a, &b);//input
        to[index] = b, pre[index] = al[a], al[a] = index++;
        to[index] = a, pre[index] = al[b], al[b] = index++;
    }
    scanf("%d", &S);//input
    queue<int>q;
    q.push(S);
    dis[S] = 0;
    used[S] = 1;
    int cnt = 1;//已经处理了1个点了
    while (!q.empty() && cnt < N) {
        memset(mk, 0, (N + 1)*sizeof(bool));
        int a = q.front();
        q.pop();
        for (int p = al[a]; p; p = pre[p])
            mk[to[p]] = 1;//先得出与哪些点是直连的
        for (int p = 1; p <= N; p++)
            if (!used[p] && !mk[p]) {//再处理非直连且没用过的点
                q.push(p);
                used[p] = 1;
                cnt++;
                dis[p] = dis[a] + 1;
            }
    }
    if (S == 1) {//下面这些只是对输出进行了一点处理 没什么特别意义
        printf("%d", dis[2]);//output
        for (int p = 3; p <= N; p++)
            printf(" %d", dis[p]);//output
    }
    else {
        printf("%d", dis[1]);//output
        for (int p = 2; p < S; p++)
            printf(" %d", dis[p]);//output
        for (int p = S + 1; p <= N; p++)
            printf(" %d", dis[p]);//output
    }
    printf("\n");//output
}
int main(void)
{
    freopen("vs_cin.txt", "r", stdin);
    freopen("vs_cout.txt", "w", stdout);

    int times;
    while (~scanf("%d", &times)) {
        while (times--) {
            scanf("%d%d", &N, &M);
            fun();
            memset(al, 0, (N + 1)*sizeof(int));
            memset(used, 0, (N + 1)*sizeof(bool));
        }
    }
}

EOF

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值