zoukankan      html  css  js  c++  java
  • 囤题 [补档]

    图论

    网络流相关

    CF 653 D

    题目大意

    给定一张(n)个点, (m)条边的有向图((n le 50), (m le 500)), 每条边都有容量限制. 你要找到至少(x)条路径, 使得每条路径点容量都为某个定值(F), 且经过任意一条边点所有路径的容量之和小于等于这条边的容量. 求(F)的最大值.

    题解

    我们令原图的边集为(E = { left< u, v, w ight> })
    二分(F)的值, 对于一个确定点(F), 我们有每条边的最大经过次数. 将这个最大经过次数作为边的容量重新建图, 得到(E' = { left< u, v, c = frac w F ight> }). 跑一遍最大流, 得到的流量即为可以选出的路径最大数量. 判断若最大流大于等于(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 e T_{|S| - i + 1}), 并且使得这个排列的权值最大. 一个排列的权值为(sum_k b_k imes [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);
    	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
    ", flow());
    }
    
    
  • 相关阅读:
    人 生 死 梦
    接口(三):
    接口(二):
    Mac下OpenCV开发环境配置(Terminal和Xcode)
    OcLint的使用
    分类Category的概念和使用流程
    @class
    内存管理
    点语法
    多态的概念和用法
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/8379698.html
Copyright © 2011-2022 走看看