P4174 [NOI2006] 最大获利

洛谷传送门

题意: 有n个中转站建造方案,每个中转站有一个建造的费用,有m个用户群,每个用户群能带来一定的收益,但是每个用户群都会指定两个中转站必须建造,问最大可能收益

思路:

最大权闭合图:

定义:在有向图中,每个点有一个权值,取一个点集,使得没有一个点有向外指的边,这样的权值最大的点集。

可以发现当我们选了一个点,这个点指向的点也一定要选。 于是可以和这道题对应起来,当我们选了一个用户群,这个用户群指定的中转站也必须建造,可以发现这里如果用最大权闭合图来做的话可以不止指定两个中转站。

最大权闭合图的建图方式:将所有点权为正的和点权为负的分开,一个和源点连容量为点权绝对值的边,另一个和汇点连容量为点权绝对值的边,跑一遍最小割(最大流),用所有正的点权和减去这个最小割就是答案

但是由于这题每个用户只指定了两个中转站,这题还可以用最大密度子图

此时每个用户群是一条边(刚才是点,)

最大密度子图:(带边权和点权)

定义:找出原图的一个子图,使得这个子图的边权比上点权的最大的比值。

采用0-1整数规划的思想来求最优解。使用二分查找的方法来求g(h)

∣ E ′ ∣ + ∣ V ′ ∣ ∣ V ′ ∣ = g \frac{|E'| + |V'|}{|V'|} = g VE+V=g , 这题要最大化 ∣ E ′ ∣ + ∣ V ′ ∣ |E'| + |V'| E+V, g取0,

建图:每个用户群要求的两个中转站直接连一条为用户群收益的边,源点到每个用户群连一条容量为偏移量的边,每个用户群到汇点连一条为 − 度 数 − 2 × 中 转 站 建 造 费 用 的 边 2 \frac{ -度数 - 2 \times {中转站建造费用的边}}{2} 22×

a n s = N × m x − m a x f l o w 2 ans= \frac{N \times mx - maxflow}{2} ans=2N×mxmaxflow (mx是边权变为正的偏移量)

最大权闭合图:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

struct Edge {
    ll from, to, cap, flow;
    Edge(ll a, ll b, ll c, ll d) : from(a), to(b), cap(c), flow(d) {}
};

struct Dinic {
    static const ll maxn = 55010;
    static const ll inf = 0x3f3f3f3f3f3f3f3f;
    ll N, M, S, T;
    vector<Edge> edges;
    vector<ll> G[maxn];
    bool vis[maxn];
    ll d[maxn];
    ll cur[maxn];

    void AddEdge(ll from, ll to, ll cap) {
        edges.push_back(Edge(from, to, cap, 0));
        edges.push_back(Edge(to, from, 0, 0));
        M = edges.size();
        G[from].push_back(M - 2);
        G[to].push_back(M - 1);
    }

    bool BFS() {
        memset(vis, 0, sizeof(vis));
        queue<ll> Q;
        Q.push(S);
        d[S] = 0;
        vis[S] = 1;
        while (!Q.empty()) {
            ll x = Q.front();
            Q.pop();
            for (ll i = 0; i < G[x].size(); i++) {
                Edge& e = edges[G[x][i]];
                if (!vis[e.to] && e.cap > e.flow) {
                    vis[e.to] = 1;
                    d[e.to] = d[x] + 1;
                    Q.push(e.to);
                }
            }
        }
        return vis[T];
    }

    ll DFS(ll x, ll a) {
        if (x == T || a == 0) return a;
        ll flow = 0, f;
        for (ll& i = cur[x]; i < G[x].size(); i++) {
            Edge& e = edges[G[x][i]];
            if (d[x] + 1 == d[e.to] &&
                (f = DFS(e.to, min(a, e.cap - e.flow))) > 0) {
                e.flow += f;
                edges[G[x][i] ^ 1].flow -= f;
                flow += f;
                a -= f;
                if (a == 0) break;
            }
        }
        return flow;
    }

    ll Maxflow(ll S, ll T) {
        this->S = S, this->T = T;
        ll flow = 0;
        while (BFS()) {
            memset(cur, 0, sizeof(cur));
            flow += DFS(S, inf);
        }
        return flow;
    }
} MF;

const int inf = 0x3f3f3f3f;

int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	
	int n, m;
	cin >> n >> m;
	int s = 0, t = n + m + 1;
	
	for(int i = 1, x; i <= n; ++ i) {
		cin >> x;
		MF.AddEdge(s, i, x);
	}
	int ans = 0;
	for(int i = 1, a, b, c; i <= m; ++ i) {
		cin >> a >> b >> c;
		ans += c;
		MF.AddEdge(i + n, t, c);
		MF.AddEdge(a, i + n, inf);
		MF.AddEdge(b, i + n, inf); 
	}
	
	cout << ans - MF.Maxflow(s, t); 
	
	return 0;
	
} 

最大密度子图

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

struct Edge {
    ll from, to, cap, flow;
    Edge(ll a, ll b, ll c, ll d) : from(a), to(b), cap(c), flow(d) {}
};

struct Dinic {
    static const ll maxn = 10000;
    static const ll inf = 0x3f3f3f3f3f3f3f3f;
    ll N, M, S, T;
    vector<Edge> edges;
    vector<ll> G[maxn];
    bool vis[maxn];
    ll d[maxn];
    ll cur[maxn];

    void AddEdge(ll from, ll to, ll cap) {
        edges.push_back(Edge(from, to, cap, 0));
        edges.push_back(Edge(to, from, 0, 0));
        M = edges.size();
        G[from].push_back(M - 2);
        G[to].push_back(M - 1);
    }

    bool BFS() {
        memset(vis, 0, sizeof(vis));
        queue<ll> Q;
        Q.push(S);
        d[S] = 0;
        vis[S] = 1;
        while (!Q.empty()) {
            ll x = Q.front();
            Q.pop();
            for (ll i = 0; i < G[x].size(); i++) {
                Edge& e = edges[G[x][i]];
                if (!vis[e.to] && e.cap > e.flow) {
                    vis[e.to] = 1;
                    d[e.to] = d[x] + 1;
                    Q.push(e.to);
                }
            }
        }
        return vis[T];
    }

    ll DFS(ll x, ll a) {
        if (x == T || a == 0) return a;
        ll flow = 0, f;
        for (ll& i = cur[x]; i < G[x].size(); i++) {
            Edge& e = edges[G[x][i]];
            if (d[x] + 1 == d[e.to] &&
                (f = DFS(e.to, min(a, e.cap - e.flow))) > 0) {
                e.flow += f;
                edges[G[x][i] ^ 1].flow -= f;
                flow += f;
                a -= f;
                if (a == 0) break;
            }
        }
        return flow;
    }

    ll Maxflow(ll S, ll T) {
        this->S = S, this->T = T;
        ll flow = 0;
        while (BFS()) {
            memset(cur, 0, sizeof(cur));
            flow += DFS(S, inf);
        }
        return flow;
    }
} MF;

const int N = 5010;
int p[N], du[N];

int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	
	int n, m;
	cin >> n >> m;
	
	for(int i = 1; i <= n; ++ i) {
		cin >> p[i];
		p[i] = -p[i];
	}	
	for(int i = 0, u, v, c; i < m; ++ i) {
		cin >> u >> v >> c;
		MF.AddEdge(u, v, c);
		MF.AddEdge(v, u, c); 
		du[u] += c, du[v] += c;
	}
	int mx = 0;
	for(int i = 1; i <= n; ++ i) {
		mx = max(mx, du[i] + 2 * p[i]);
	}
	for(int i = 1; i <= n; ++ i) {
		MF.AddEdge(0, i, mx);
		MF.AddEdge(i, n + 1, mx - du[i] - 2 * p[i]); 
	}
	cout << (n * mx - MF.Maxflow(0, n + 1)) / 2;
} 
NOI2006是指2006年的全国信息学奥林匹克竞赛。"聪明的导游"是一道在比赛中出现的题目,需要我们编写程序进行求解和数据分析。 首先,我们需要从官方网站或相关渠道下载与“聪明的导游”题目相关的数据。这些数据可能包括景点的名称、编号、导游线路的长度、舒适度,以及可能的限制条件和特殊要求等。 接下来,我们可以使用编程语言(如C++、Python等)来编写一个程序,对这些数据进行处理和分析。这个问题可以被抽象为一个图论问题,其中景点可以表示为图中的节点,导游线路可以表示为图中的边。 我们可以使用图的遍历算法(如深度优先搜索或广度优先搜索)来寻找最佳的导游线路。我们可以用一个数组和一个矩阵来表示该导游线路,其中数组存储已经访问过的景点,矩阵表示两个不同景点之间的距离。 在程序中,我们可以使用适当的数据结构来存储和处理这些数据,例如数组、图、队列等。我们可以使用动态规划等算法来优化计算效率,从而找到最优的导游线路。 最后,我们可以根据程序的运行结果分析和展示最佳导游线路的路径和特点。我们可以输出导游线路的长度、各个景点的编号和名称,以及其舒适度等信息。 总之,要下载NOI2006比赛中的“聪明的导游”题目数据,我们需要从官方渠道获取相关数据,编写一个程序来处理和分析这些数据,最终找到最佳导游线路的解决方案。通过合理的算法设计和数据结构选择,我们可以有效地解决这个问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值