1. 程式人生 > >囤題 [補檔]

囤題 [補檔]

mar 邊集 git scan min pac 其中 limit ear

圖論

網絡流相關

CF 653 D

題目大意

給定一張\(n\)個點, \(m\)條邊的有向圖(\(n \le 50\), \(m \le 500\)), 每條邊都有容量限制. 你要找到至少\(x\)條路徑, 使得每條路徑點容量都為某個定值\(F\), 且經過任意一條邊點所有路徑的容量之和小於等於這條邊的容量. 求\(F\)的最大值.

題解

我們令原圖的邊集為\(E = \{ \left< u, v, w \right> \}\)
二分\(F\)的值, 對於一個確定點\(F\), 我們有每條邊的最大經過次數. 將這個最大經過次數作為邊的容量重新建圖, 得到\(E‘ = \{ \left< u, v, c = \frac w F \right> \}\)

. 跑一遍最大流, 得到的流量即為可以選出的路徑最大數量. 判斷若最大流大於等於\(x\), 則對於當前的\(F\), 有可行方案, 否則沒有可行方案.

#include <cstdio>
#include <cctype>
#include <cstring>
#include <climits>
#include <deque>
#include <algorithm>

using namespace std;
inline int getInt()
{
    int a = 0, sgn = 1; char c;
    while
(! isdigit(c = getchar())) if (c == ‘-‘) sgn *= -1; while (isdigit(c)) a = a * 10 + c - ‘0‘, c = getchar(); return a * sgn; } typedef double D; typedef long long LL; const D EPS = 1e-8; const LL INF = (LL)1e18; const int N = 50, M = 500; int n, m, x; struct Record { int u, v, w; }rec[M + 7]; struct
edge { int v, nxt; LL w; }edg[M * 2 + 7]; int tot, hd[N + 7], dis[N + 7]; inline void addEdge(int u, int v, LL w) { edg[tot].v = v; edg[tot].w = w; edg[tot].nxt = hd[u]; hd[u] = tot ++; } inline int BFS() { memset(dis, -1, sizeof dis); dis[1] = 0; deque<int> que; que.push_back(1); while (! que.empty()) { int u = que.front(); que.pop_front(); if (u == n) return 1; for (int p = hd[u]; ~ p; p = edg[p].nxt) if (edg[p].w && dis[edg[p].v] == -1) dis[edg[p].v] = dis[u] + 1, que.push_back(edg[p].v); } return 0; } LL DFS(int u, LL flw) { if (u == n) return flw; LL usd = 0; for (int p = hd[u]; ~ p; p = edg[p].nxt) if (edg[p].w && dis[edg[p].v] == dis[u] + 1) { int v = edg[p].v; LL cur = DFS(v, min(edg[p].w, flw - usd)); edg[p].w -= cur; edg[p ^ 1].w += cur; usd += cur; if (usd == flw) return usd; } return usd; } inline LL flow() { LL sum = 0; while (BFS()) sum += DFS(1, INF); return sum; } int main() { #ifndef ONLINE_JUDGE freopen("bear.in", "r", stdin); freopen("bear.out", "w", stdout); #endif n = getInt(); m = getInt(); x = getInt(); for (int i = 0; i < m; ++ i) rec[i]. u = getInt(), rec[i].v = getInt(), rec[i].w = getInt(); D L = 1e-5, R = 1e6; while (R - L > EPS) { D mid = (L + R) / 2; memset(hd, -1, sizeof hd); tot = 0; for (int i = 0; i < m; ++ i) addEdge(rec[i].u, rec[i].v, (LL)rec[i].w / mid), addEdge(rec[i].v, rec[i].u, 0); if (flow() < x) R = mid; else L = mid; } printf("%.7lf", L * (D)x); }

CF 884 F

題目大意

給定一個由小寫字母組成的串\(S\)(\(|S| \le 100\)), 每個位置有一個權值\(b_i\), 你要找到一個原串的排列\(T\), 滿足對於每個\(i\)都有\(T_i \ne T_{|S| - i + 1}\), 並且使得這個排列的權值最大. 一個排列的權值為\(\sum_k b_k \times [T_k = S_k]\). 求這個權值.

保證存在至少一組合法的排列.

題解

最大費用最大流.

建立\(26\)個節點分別代表\(26\)個字母. 統計每個字母的出現次數, 從源點向每個字母的節點連一條容量為出現次數, 費用為\(0\)的邊.

接著我們再建立\(\frac{|S|}2\)個節點, 代表不能相互沖突的一對位置. 考慮從字母向位置連邊, 容量顯然為\(1\), 費用則比較麻煩: 首先假如當前字母不等於\(S_i\)\(S_{|S| - i + 1}\), 則費用為\(0\); 假如等於其中的一個, 則費用為\(b\); 假如和這兩個位置都相等, 則費用為\(\max(b_i, b_{|S| - i + 1})\).

最後, 從每個位置向匯點連一條容量為\(2\), 費用為\(0\)的邊.

跑一遍最大費用的最大流即可.

#include <cstdio>
#include <cstring>
#include <deque>
#include <climits>

using namespace std;
const int N = 100;
const int V = N / 2 + 107;
const int E = 31 * N + 107;
int n;
int cnt[27];
int str[N + 7], b[N + 7];
struct edge
{
    int v, w, c, nxt;
}edg[E];
int hd[V], tot;
int pre[V], rec[V], inQueue[V], dis[V];
inline void addEdge(int u, int v, int w, int c) 
{
    edg[tot].v = v; edg[tot].w = w; edg[tot].c = c; edg[tot].nxt = hd[u]; hd[u] = tot ++;
    edg[tot].v = u; edg[tot].w = 0; edg[tot].c = - c; edg[tot].nxt = hd[v]; hd[v] = tot ++;
}
inline int SPFA()
{
    int t = 26 + n / 2 + 1;
    memset(pre, -1, sizeof pre);
    deque<int> que; que.push_back(0);
    memset(inQueue, 0, sizeof inQueue); inQueue[0] = 1;
    memset(dis, -127, sizeof dis); dis[0] = 0;
    while (! que.empty())
    {
        int u = que.front(); que.pop_front(); inQueue[u] = 0;
        for (int p = hd[u]; ~ p; p = edg[p].nxt) if (edg[p].w && dis[u] + edg[p].c > dis[edg[p].v])
        {
            int v = edg[p].v;
            dis[v] = dis[u] + edg[p].c; pre[v] = u; rec[v] = p;
            if (! inQueue[v]) inQueue[v] = 1, que.push_back(v);
        }
    }
    int mn = INT_MAX;
    for (int p = t; ~ pre[p]; p = pre[p]) mn = min(mn, edg[rec[p]].w);
    for (int p = t; ~ pre[p]; p = pre[p]) 
        edg[rec[p]].w -= mn, edg[rec[p] ^ 1].w += mn;
    return dis[t];
}
inline int flow()
{
    int sum = 0;
    while (1) { int tmp = SPFA(); if (tmp < - (int)2e9) return sum; sum += tmp; }
}
int main()
{

#ifndef ONLINE_JUDGE

    freopen("anti.in", "r", stdin);
    freopen("anti.out", "w", stdout);
    
#endif

    scanf("%d\n", &n);
    for (int i = 1; i <= n; ++ i) ++ cnt[str[i] = (getchar() - ‘a‘ + 1)];
    for (int i = 1; i <= n; ++ i) scanf("%d", b + i);
    memset(hd, -1, sizeof hd); tot = 0;
    for (int i = 1; i <= 26; ++ i) addEdge(0, i, cnt[i], 0);
    for (int i = 1; i <= 26; ++ i) for (int j = 1; j <= n >> 1; ++ j) 
        addEdge(i, 26 + j, 1, max((str[j] == i) * b[j], (str[n - j + 1] == i) * b[n - j + 1]));
    for (int i = 1; i <= n >> 1; ++ i) addEdge(26 + i, 26 + n / 2 + 1, 2, 0);
    printf("%d\n", flow());
}

囤題 [補檔]