AcWing 1165 單詞環
阿新 • • 發佈:2022-03-26
本題同樣是考察\(01\)分數規劃,只不過難度較上題有所提升。首先需要考慮的是如何建圖,如果常規的用每個字串作為圖中的節點,最多會有\(10w\)個節點,邊數也可能高達\(100\)億級別,顯然難以承受,而且以字串作為節點,還需要挨個比較每個字串的末尾兩個字元與其它字串的開頭兩個字元是否相等,這也將耗費大量的時間。
我們知道,有小寫字母構成的兩個字元最多有\(26 * 26 = 676\)種可能,而這十萬字串中間的內容並不重要,我們只關心每個字串的開始兩個字元、末尾兩個字元以及字串的長度。這樣我們讀入一個字串時,將其開頭長度為\(2\)的字串作為一個節點,末尾長度為\(2\)的字串作為另一個節點,兩個節點間有向邊的長度就是該字串的長度。這種巧妙的建圖方式不僅使得節點數驟減,邊數不超過限制\(10w\)
第二步就是按照\(01\)分數規劃的解題思路推公式,環串的平均長度最大,等價於\(∑s_i / ∑1\)最大,其中\(s_i\)表示環中各邊的長度,\(∑1\)就是環上的邊數,要判斷對於某個\(mid\)
最後本題時間卡得很緊,如果要等待出現一條路徑涉及邊的條數達到\(676\)再確定存在負環,就會超時,這裡就要用到玄學優化了,一般經驗值取節點數的兩倍,這裡邊比較多就取\(100000\),即被鬆弛的總次數達到\(100000\)時就按照經驗判定為存在負環,結束\(spfa\)演算法。也可以將\(spfa\)中的佇列換成棧,在實現上相當於每次都從隊尾取元素,這樣一來,一旦存在環,就會很快的去鬆弛環上的下一個節點,更快的結束演算法。另外,對於不存在負權的情況,只需要判斷下\(mid = 0\)
#include <bits/stdc++.h>
using namespace std;
// n=26 26*26=676 這裡N設為700
const int N = 700, M = 100010;
int m; //邊的數量
int cnt[N], q[N];
double dist[N];
bool st[N];
//鄰接表
int idx, h[N], e[M], w[M], ne[M];
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
bool check(double mid) {
memset(dist, 0x3f, sizeof dist);
memset(cnt, 0, sizeof cnt);
memset(st, false, sizeof st);
queue<int> q;
for (int i = 0; i < 676; i++) {
q.push(i);
st[i] = true;
}
//整體入佇列次數,676*10表示所有點都入隊10次了,還沒有找到解
int count = 0;
while (q.size()) {
int u = q.front();
q.pop();
st[u] = false;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (dist[j] < dist[u] + w[i] - mid) {
dist[j] = dist[u] + w[i] - mid;
cnt[j] = cnt[u] + 1;
if (++count > 10000 || cnt[j] >= 676) return true;
if (!st[j]) {
q.push(j);
st[j] = true;
}
}
}
}
return false;
}
int main() {
string s;
while (cin >> m, m) {
//多組測試資料,清空鄰接表
memset(h, -1, sizeof h);
idx = 0;
//讀入每條邊
for (int i = 0; i < m; i++) {
cin >> s;
//不夠長,沒用
if (s.size() < 2) continue;
//模擬節點號
int a = (s[0] - 'a') * 26 + s[1] - 'a';
int b = (s[s.size() - 2] - 'a') * 26 + s[s.size() - 1] - 'a';
//建圖,有向圖
add(a, b, s.size());
}
if (!check(0))
puts("No solution");
else {
double l = 0, r = 1000;
while (r - l > 1e-4) {
double mid = (l + r) / 2;
if (check(mid))
l = mid;
else
r = mid;
}
printf("%.2lf\n", l);
}
}
return 0;
}