2019 ICPCアジア上海地域コンテスト(リプレイ)M-Blood Pressure Game(コストフローと計算への貢献)

リンク:https
://ac.nowcoder.com/acm/contest/4370/M出典:Niuke
 

タイトル説明

1年前にACMerだったJBerの1Guguluが上海大学に来て、再び国際大学プログラミングコンテストに参加しました。しかし、ググルが上海大学に来るたびに、彼は常に鉄メダルを獲得し、この競争をJBのように考えていました。彼の痛みを和らげるために、ググルは上海ディズニーランドパークに行き、ジェットコースターを楽しんでいました。ググルは高血圧の気持ちが大好きで、間違った答えや制限時間を超えたことなどをすべて忘れて幸せなゆるい鳥のように感じます。

ジェットコースターのパスは、配列{a1、a2、a3、…、an} \ {a_1、a_2、a_3、…、a_n \} {として表すことができるさまざまな高さのターニングポイントのリストと見なすことができます。サイズn {n} nのa1、a2、a3、…、an}、およびジェットコースターのゲーム後のググルの最終血圧は、n-間の差のすべての絶対値の合計としてカウントされます。 1 {n-1} n-1ペアの隣接する配列番号、つまり∑i = 1n-1∣ai-ai + 1∣ \ displaystyle \ sum_ {i = 1} ^ {n-1} \ left | a_i --a_ {i + 1} \ right | i = 1∑n−1 ∣ai −ai + 1∣。

ググルは常に鉄メダルを獲得しており、常に鉄メダルを獲得しているため、ジェットコースターを何度も何度も獲得しています。しかし、ゲームをプレイするにつれて、自分を幸せにすることができる血圧の値に対する彼のしきい値は増え続けています。その結果、上海ディズニーランドパークのジェットコースターはもはやググルのニーズを満たすことができません。

したがって、Guguluは、このパスにm個の追加のターニングポイントのセットを任意の順序で追加することにしましたが、元のパスの距離の現実を考慮すると、最大で1つのターニングポイントを任意の2つの間の元の位置に追加できます。配列内の元の要素(および最大で1つがヘッド、最大で1つがテール)。ググルは血圧をできるだけ高くしたいと考えており、{1,2,3、…、m} {\ {1、2、3、…を追加したときに最大で血液が到達できる量を知りたいと考えています。 、m \}} {1,2,3、…、m} nextraジェットコースターがポイントをパスに変えます。

別のJBerであるあなたは、ググルが可能な限り最高の血圧を得るのに十分賢いと確信しています。ググルの命を救うために、事前に適切な心臓専門医との約束をするために正確な数を計算することは非常に重要です。この問題を解決する必要があります!ググルの血圧は制御不能になりつつあります!

説明を入力してください:

入力の最初の行は、テストケースの数T \ mathbf {T} T(1≤T≤10001\ leq \ mathbf {T} \leq10001≤T≤1000)を示します。T \ mathbf {T} Tテストケースが続きます。
テストケースごとに、最初の行には2つの整数n {n} n(1≤n≤6001\ leq n \leq6001≤n≤600)とm {m} m(1≤m≤n+ 11 \ leq)が含まれています。 m \ leqn +11≤m≤n+ 1)
次に、2行目にn {n} n個の整数{a1、a2、a3、…、an} \ {a_1、a_2、a_3、…、a_n \ } {a1、a2、a3、…、an}(1≤ai≤109)(1 \ leq a_i \ leq 10 ^ 9)(1≤ai≤109)、n {n} nを表すオリジナルのジェットコースターのターニングポイント。次に、3行目には、m {m} m個の整数{b1、b2、b3、…、bm} \ {b_1、b_2、b_3、…、b_m \} {b1、b2、b3、… 、bm}(1≤bi≤109)(1 \ leq b_i \ leq 10 ^ 9)(1≤bi≤109)、追加されるm {m} mのジェットコースターのターニングポイントを示します。
エキサイティングなジェットコースターであるため、2つの配列のすべてのn + m {n + m} n + m整数がペアごとに異なることが保証されています。
n {n} nが100 {100} 100を超えるテストケースは最大で10 {10} 10あります。

出力の説明:

テストケースごとに、3行を出力します。最初の行には「Case#x:」が含まれています。ここで、x {x} xはテストケース番号です(1 {1} 1から始まります)。2行目には、ググルが{1、2、3、…、m}のジェットコースターのターニングポイントをパスに追加した場合に到達できる血圧を表すm {m} mの整数があるはずです。3行目には、すべてのm {m} mの追加のターニングポイントを追加した場合、最終的なジェットコースターパスの高さを表すn + m {n + m} n + mの整数が必要です。複数の解決策がある場合は、それらのいずれかを出力します。

例1

入る

コピー62 3 5 11 10 3 1 4 1 1 2 3 4 5 4 2 1 2 3 4 5 6 4 5 1 2 3 4 5 6 7 8 9 4 4 10 50 3 6 1 9 23 5 4 2 10 50 3 6 9 23

6 
2 3 
5 11 
10 3 1 
4 1 
1 2 3 4 
5 
4 2 
1 2 3 4 
5 6 
4 5 
1 2 3 4 
5 6 7 8 9 
4 4 
10 50 3 6 
1 9 23 5 
4 2 
10 50 3 6 
9 23

出力

复制ケース#1:16 22 27 10 5 1 11 3ケース#2:9 1 5 2 3 4ケース#3:11 15 1 6 2 5 3 4ケース#4:17 27 33 38 39 5 1 9 2 8 3 7 4 6ケース#5:124142147150 5 10 1 50 3 23 6 9ケース#6:124127 10 50 3 23 6 9

ケース#1:
16 22 27 
10 5 1 11 3
ケース#2:
9 
1 5 2 3 4
ケース#3:
11 15 
1 6 2 5 3 4
ケース#4:
17 27 33 38 39 
5 1 9 2 8 3 7 4 6
ケース#5:
124142147150 
5 10 1 50 3 23 6 9
ケース#6:
124127 
10 50 3 23 6 9

本旨:

長さNのシーケンスと長さMのシーケンスを指定します。これらのM個の番号を最初のシーケンスに挿入する必要があります。2つの番号の間に挿入できる番号は1つだけです(左または右に配置できます)。 、挿入の順序は任意であり、シーケンスの重みは隣接する番号間の差の絶対値の合計です。最初の行にM番号を出力し、i番目の番号はi番号を挿入した後の最大重み値を表し、2行目はM番号を挿入した後の最大重み値のシーケンスを出力します。

解決:

挿入可能なN + 1の位置に対応して、M個の番号に接続できるN + 1のポイントがあります。最初に、最初のシーケンスの重みを計算します。ポイントが挿入されると、その寄与は挿入後の重みになります-元の重み、つまりマッピング方法は次のとおりです。ソースポイントはMポイントに接続され、フローは1、コストは0、MポイントはN + 1ポイントに接続され、フローは1、コストは寄与、N +1ポイントはシンクに接続されます。ポイント、フローは1、コストは0、最大コストフローはM回実行され、最後にシーケンスが残りのネットワークに出力されます。

この質問は私たちの学校の大物のチームによって出されました(それがネットワークストリームであることを知っているという前提の下で)、そしてアイデアはすべて彼から学びました、私はまだあまりにも良いQAQです

 アクセプトされたコード

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;

#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define pir pair <int, int>
#define MK(x, y) make_pair(x, y)
#define MEM(x, b) memset(x, b, sizeof(x))
#define MPY(x, b) memcpy(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))

typedef long long ll;
const int Mod = 1e9 + 7;
const int N = 1210;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
inline ll dpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % Mod; b >>= 1; t = (t*t) % Mod; }return r; }
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t); b >>= 1; t = (t*t); }return r; }

struct Edge
{
	ll f, w;
	int r, v;
};
vector <Edge> G[N];
ll dis[N], flow[N];
ll mxflow, mxcost;
int n, m, sp, tp;
int pre[N], lst[N];
bool vis[N];
int a[N], b[N];

void Init() {
	sp = 0, tp = n + m + 2;
	for (int i = sp; i <= tp; i++)
		G[i].clear();
	mxflow = mxcost = 0;
}
void Add(int u, int v, ll f, ll w) {
	G[u].push_back({ f, w, SZ(G[v]), v });
	G[v].push_back({ 0, -w, SZ(G[u]) - 1, u });
}
bool Spfa() {   // 找到一条长短路的增广
	queue <int> q;
	for (int i = sp; i <= tp; i++) {
		dis[i] = -LINF;
		flow[i] = LINF;
		vis[i] = false;
	}
	q.push(sp);
	vis[sp] = true;
	dis[sp] = 0, pre[tp] = -1;

	while (!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = false;

		for (int i = 0; i < SZ(G[u]); i++) {
			ll f = G[u][i].f, w = G[u][i].w;
			int v = G[u][i].v;
			if (f && dis[v] < dis[u] + w) {
				dis[v] = dis[u] + w;
				pre[v] = u;       // 前驱点
				lst[v] = i;       // 当前边的vector下标
				flow[v] = min(flow[u], f);
				if (!vis[v]) {
					vis[v] = true;
					q.push(v);
				}
			}
		}
	}
	return pre[tp] != -1;
}

int main()
{
	int T;
	cin >> T;
	for (int Case = 1; Case <= T; Case++) {
		sc("%d %d", &n, &m);
		Init();

		ll ans = 0;
		for (int i = 1; i <= n; i++) {
			sc("%d", &a[i]);
			if (i > 1)
				ans += abs(a[i] - a[i - 1]);
		}

		// 源点->待插入点
		for (int i = 1; i <= m; i++) {
			sc("%d", &b[i]);
			Add(sp, i, 1, 0);
		}

		// 待插入点对原序列的贡献
		for (int i = 1; i <= m; i++) {
			for (int j = 1; j <= n + 1; j++) {
				ll w;
				if (j == 1)
					w = abs(b[i] - a[1]);
				else if (j == n + 1)
					w = abs(b[i] - a[n]);
				else
					w = abs(b[i] - a[j - 1]) + abs(b[i] - a[j]) - abs(a[j] - a[j - 1]);
				// 待插入->原序列
				Add(i, m + j, 1, w);
			}
		}

		// 原序列->汇点
		for (int i = 1; i <= n + 1; i++)
			Add(m + i, tp, 1, 0);

		printf("Case #%d:\n", Case);
		for (int i = 1; i <= m; i++) {
			Spfa();
			int now = tp;
			mxflow = flow[tp];
			mxcost = flow[tp] * dis[tp];
			while (now != sp) {
				G[pre[now]][lst[now]].f -= mxflow;
				G[now][G[pre[now]][lst[now]].r].f += mxflow;
				now = pre[now];
			}
			ans += mxcost;
			printf("%lld ", ans);
		}
		puts("");


		for (int i = m + 1; i <= n + m + 1; i++) {
			for (auto it : G[i]) {
				int v = it.v;
				if (v >= 1 && v <= m && it.f) {
					printf("%d ", b[v]);
					break;
				}
			}
			if (i == n + m + 1)
				puts("");
			else
				printf("%d ", a[i - m]);
		}
	}
	return 0;  // 改数组大小!!!用pair记得改宏定义!!!
}

 

おすすめ

転載: blog.csdn.net/weixin_43851525/article/details/107606512