洛谷 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; }