CodeForces 1155F Delivery Oligopoly
題目大意
給定一個已經是邊雙聯通分量的圖,要求刪掉最多的邊,使得最終得到的圖是也是一個邊雙聯通分量。輸出保留的邊。
分析
考慮我們如何構造出一個邊雙聯通分量。
我們發現最終的答案一定是一個環帶上一個環的樣子(有點像糖葫蘆)。
於是考慮強制以爲起點,每次向上面加上一條鏈。
考慮如何按照這樣的方式來計算答案。
首先預處理一個狀態,表示從到能否經過集合中的點互相到達。
那麼我們就可以定義狀態爲在集合中的點形成一個邊雙聯通分量。
轉移顯然,注意記錄下轉移路徑,方便輸出。 細節巨多。。。
參考代碼
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int Maxn = 14;
const int INF = 0x3f3f3f3f;
int N, M;
vector<int> G[Maxn + 5];
void addedge(int u, int v) {
G[u].push_back(v), G[v].push_back(u);
}
int f[(1 << Maxn) + 5];
pair<int, int> last[(1 << Maxn) + 5];
int lasts[(1 << Maxn) + 5];
bool go[Maxn + 3][Maxn + 3][(1 << Maxn) + 3];
int lastnode[Maxn + 3][Maxn + 3][(1 << Maxn) + 3];
inline int lowbit(int x) {return x & (-x);}
inline int bitcount(int x) {
int ret = 0;
while(x) ret++, x -= lowbit(x);
return ret;
}
int main() {
#ifdef LOACL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
scanf("%d %d", &N, &M);
for(int i = 1; i <= M; i++) {
int u, v;
scanf("%d %d", &u, &v);
addedge(u, v);
}
memset(f, 0x3f, sizeof f);
for(int u = 1; u <= N; u++)
for(auto v : G[u])
go[u][v][0] = true, lastnode[u][v][0] = u;
for(int s = 0; s < (1 << N); s++)
for(int i = 1; i <= N; i++)
for(int j = 1; j <= N; j++) {
if((s & (1 << (i - 1))) || (s & (1 << (j - 1))) || i == j || !go[i][j][s])
continue;
for(auto k : G[j]) {
if(s & (1 << (k - 1))) continue;
if(k == lastnode[i][j][s]) continue;
int t = s ^ (1 << (j - 1));
if(!go[i][k][t])
go[i][k][t] = true, lastnode[i][k][t] = j;
}
}
f[1] = 0;
for(int s = 0; s < (1 << N); s++)
for(int t = s; t; t = (t - 1) & s) {
int lst = s ^ t;
int cnt = bitcount(t) + 1;
if(f[lst] + cnt >= f[s]) continue;
for(int u = 1; u <= N; u++) {
if(!(lst & (1 << (u - 1)))) continue;
for(int v = 1; v <= N; v++) {
if(!(lst & (1 << (v - 1)))) continue;
if(go[u][v][t]) {
f[s] = f[lst] + cnt;
last[s] = make_pair(u, v);
lasts[s] = t;
}
}
}
}
if(f[(1 << N) - 1] == INF) {
puts("-1");
return 0;
}
printf("%d\n", f[(1 << N) - 1]);
int t = (1 << N) - 1;
while(t != 1) {
int lst = lasts[t];
int x = last[t].first, y = last[t].second;
t ^= lst;
while(lst) {
int z = lastnode[x][y][lst];
printf("%d %d\n", y, z);
lst ^= (1 << (z - 1));
y = z;
}
printf("%d %d\n", x, y);
}
return 0;
}