洛谷 P1330 封锁阳光大学

3 篇文章 0 订阅
1 篇文章 0 订阅

传送门:https://www.luogu.org/problemnew/show/P1330

        很神奇的做法,之前没有仔细思考,贪心+优先队列去找尽可能连接多条边的点,但其实是有问题的,这样贪心的结果很可能是没有解的,而选取其他的没那么多条边的点去操作,反而是可能有解的,所以贪心不可取。

         看了大佬们的思路才理解到这个题可以这样设计(思路借鉴的,已注明转载),这个题可以分为若干个联通图,那么我们发现,对于某一条边来说,如果他连接的一个节点被选取了,那么他的另一个节点一定是没有被选取的,那么利用这个思路,我们发现,对于一个联通图来说,我们随意一个节点之后,剩下的节点的状态一定是全部确定的,因为图联通,随意一个点经过一条路径一定可以到达联通图上的任意一个点,那么这个图就只有两个状态了,第一个是我们选取的那个节点被使用,和我们选取的那个节点不被使用,那么两种图我们取其中最小的使用个数,然后对个联通图的结果求和就是我们所需要的答案。

因为关系是一层一层推进的,所以bfs写法特别的方便,而且如果我们的下一层节点的使用情况已经标记,那么需要判断和当前我们的节点标记是否一致,如果一致说明这个方法会有一条边两个节点都选取了的情况,那么输出impossible即可。

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int gcd(int a,int b){if (b == 0) return a; return gcd(b , a%b);}
int lcm(int a, int b){ return a/gcd(a,b)*b;}
inline int read(){
    int f = 1, x = 0;char ch = getchar();
    while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}
const int maxn = 1e5 + 10;
int vis[maxn];
vector<int> v[maxn];
queue<int> q,q1;
int main(){
    int n = read(),m = read(),sum = 0;
    for (int i = 0; i < m; ++i) {
        int a = read(), b = read();
        v[a].push_back(b);
        v[b].push_back(a);
    }
    for (int i = 1; i <= n; ++i) {
        if (vis[i] == 0){
            q.push(i);
            vis[i] = 1;
            int cnt1 = 0,cnt2 = 0;// 1 代表染色,-1代表不染色
            while(!q.empty()) {
                int now = q.front(), tag = 1;
                q.pop();
                if (vis[now] == 1) cnt1++;
                else cnt2++;
                for (int i = 0; i < v[now].size(); ++i) {
                    if (vis[v[now][i]] == vis[now]) {
                        cout << "Impossible" << endl;
                        return 0;
                    }
                    if (vis[v[now][i]] == 0) {
                        q.push(v[now][i]);
                        vis[v[now][i]] = -vis[now];
                    }
                }
            }
            while (!q.empty()) q.pop();
            sum += min(cnt1,cnt2);
        }
    }
    cout << sum << endl;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值