HDU6611 K Subsequence - 費用流

K Subsequence

Time Limit: 2000/2000 MS (Java/Others)   Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 217   Accepted Submission(s): 28

Problem Description

Master QWsin is dating with Sindar. And now they are in a restaurant, the restaurant has n dishes in order. For each dish, it has a delicious value ai. However, they can only order k times. QWsin and Sindar have a special ordering method, because they believe that by this way they can get maximum happiness value.

Specifically, for each order, they will choose a subsequence of dishes and in this subsequence, when i<j, the subsequence must satisfies ai≤aj. After a round, they will get the sum of the subsequence and they can’t choose these dishes again.

Now, master QWsin wants to know the maximum happiness value they can get but he thinks it’s too easy, so he gives the problem to you. Can you answer his question?

Input

There are multiple test cases. The first line of the input contains an integer T, indicating the number of test cases. For each test case:

First line contains two positive integers n and k which are separated by spaces.

Second line contains n positive integer a1,a2,a3…an represent the delicious value of each dish.

1≤T≤5

1≤n≤2000

1≤k≤10

1≤ai≤1e5

Output

Only an integer represent the maximum happiness value they can get.

Sample Input

1

9 2
5 3 2 1 4 2 1 4 6

Sample Output

22

Source

2019 Multi-University Training Contest 3

 
 

題目大概意思:

給出一個長度爲 N(1N2000)N(1≤N≤2000) 的整數序列,第 ii 個整數的值爲 ai(1ai105)a_i(1≤a_i≤10^5) ,要求選出至多 k(1k10)k(1≤k≤10) 個沒有公共元素的非降序子序列,即對於每個子序列,滿足 aiaj(i&lt;j)a_i≤a_j(i&lt;j) ,問這些子序列的和最大爲多少。

 
 

分析:

k=1k=1 的簡單情況下,問題退化爲求序列的最大非降序子序列的和。這個問題我們可以使用動態規劃的方法在時間複雜度 O(n2)O(n^2) 下求出,即設 dp[i]dp[i] 爲以第 ii 個元素爲首元素的最大非降子序列的和,那麼容易得到狀態轉移方程爲:

dp[i]=max(dp[j])+ai,i&lt;jdp[i]=max(dp[j])+a_i,i&lt;jaiaja_i≤a_j

可是當 k&gt;1k&gt;1 時,我們是無法簡單地通過進行 kk 次貪心選取和最大的非降子序列來得到最優解的,例如下面這組數據:

N=10,k=2,{ai}={1,2,3,4,3,2,1,2,3,4}N=10,k=2,\{a_i\}=\{1,2,3,4,3,2,1,2,3,4\}

第一次選取時,和最大的子序列爲: {a1,a2,a3,a5,a9,a10}={1,2,3,3,3,4}\{a_1,a_2,a_3,a_5,a_9,a_{10}\}=\{1,2,3,3,3,4\} ,和爲 1616 .

第二次選取時,和最大的子序列爲: {a6,a8}={2,2}\{a_6,a_8\}=\{2,2\} ,和爲 44 .

最終我們會得到 2020 的錯誤答案,而實際上,選出的兩個子序列應該爲:

{a1,a2,a3,a4,a10}={1,2,3,4,4}\{a_1,a_2,a_3,a_4,a_{10}\}=\{1,2,3,4,4\} ,和爲 1414 .

{a6,a8,a9}={2,2,3}\{a_6,a_8,a_9\}=\{2,2,3\} 和爲 77 .

正確答案是 2121 .

於是我們觀察這些子序列,貪心法選出的第一條子序列是 {a1,a2,a3,a5,a9,a10}={1,2,3,3,3,4}\{a_1,a_2,a_3,a_5,a_9,a_{10}\}=\{1,2,3,3,3,4\} ,選擇了最優解中不會選擇的 a5=3a_5=3 ;原序列中還剩下 {a4,a6,a7,a8}={4,2,1,2}\{a_4,a_6,a_7,a_8\}=\{4,2,1,2\} , 缺少了最優解的第二個子序列中的 a9=3a_9=3 ,如果我們把貪心法選出的第一條子序列中的 {a5,a9}\{a_5,a_9\} 刪去,添加 a4=4a_4=4 ,再選擇 {a6,a8,a9}\{a_6,a_8,a_9\} 作爲新的子序列,那麼就可以得到最優解了。這種刪去前一次遍歷的最優解中的元素來構造總體最優解的思想,可以使用費用流來實現。由於這裏是求最大和,那麼我們可以通過求邊權取負的最小費用流來計算最大費用流:

  1. 把每個序列中的每個元素拆分爲 22 個節點(出節點和入節點),連一條出節點到入節點的容量爲 11 ,費用爲 ai-a_i 的邊;

  2. 遍歷序列,對每一對 i&lt;ji&lt;jaiaja_i≤a_j 的元素,連一條 ii 的出節點到 jj 的入節點的容量爲 11 ,費用爲 00 的邊;

  3. 從超級源點向每一個入節點連容量爲 11 ,費用爲 00 的邊,從每一個出節點向超級匯點連一條容量爲 11 ,費用爲 00 的邊。

  4. 求從超級源點到超級匯點的流量爲 kk 的最小費用,將該費用取負即爲所求答案。

 
 

需要注意的是,由於 NN 可能高達 2×1032×10^3 ,邊的數量可能高達 n(n1)2\frac{n(n-1)}{2} ,在計算最小費用流時若採用 BellmanFordBellman-Ford 算法 或是 SPFASPFA 算法 求最小費用路徑則很有可能會超出時間限制,因此這裏應在初始時使用 SPFASPFA 算法 求出初始勢,再使用 DijkstraDijkstra 算法 求此後的最小費用路徑。

 
 
下面貼代碼:

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;


const int INF = 1 << 28;
const int MAX_V = 4050;

struct P
{
	int d;
	int num;
	bool operator < (const P& y)const
	{
		return d > y.d;
	}
};

struct Edge
{
	int to;
	int cap;
	int cost;
	int rev;
};
vector<Edge> G[MAX_V];
int h[MAX_V];
int dist[MAX_V];
int prevv[MAX_V], preve[MAX_V];

void add_edge(int from, int to, int cap, int cost);

int min_cost_flow(int s, int t, int f, int V);

int _min(const int& a, const int& b);

int A[MAX_V];

int main()
{
	int T, N, K;
	scanf("%d", &T);

	while (T--)
	{
		scanf("%d%d", &N, &K);

		for (int i = 0; i < N; ++i)
		{
			scanf("%d", A + i);
		}
		int mS = 2 * N, mT = 2 * N + 1;
		for (int i = 0; i < N; ++i)
		{
			const int& cur = A[i];
			add_edge(mS, i, 1, 0);
			add_edge(i, N + i, 1, -cur);
			add_edge(N + i, mT, 1, 0);
			for (int j = i + 1; j < N; ++j)
			{
				if (A[j] >= cur)
				{
					add_edge(N + i, j, 1, 0);
				}
			}
		}

		printf("%d\n", -min_cost_flow(mS, mT, K, 2 * N + 2));

		for (int i = 2 * N + 1; i >= 0; --i)
		{
			G[i].clear();
		}
	}


	return 0;
}

void add_edge(int from, int to, int cap, int cost)
{
	Edge e1 = { to,cap,cost,G[to].size() };
	Edge e2 = { from,0,-cost,G[from].size() };

	G[from].push_back(e1);
	G[to].push_back(e2);
}

int min_cost_flow(int s, int t, int f, int V)
{
	P m, ms;
	int res = 0;

	queue<int> q;
	fill(h, h + V, INF);
	h[s] = 0;
	q.push(s);
	while (!q.empty())
	{
		int v = q.front();
		q.pop();
		dist[v] = 0;

		int deg = G[v].size();
		for (int i = 0; i < deg; i++)
		{
			Edge& e = G[v][i];
			if (e.cap)
			{
				int d = h[v] + e.cost;
				if (d < h[e.to])
				{
					h[e.to] = d;
					if (!dist[e.to])
					{
						dist[e.to] = 1;
						q.push(e.to);
					}
				}
			}
		}
	}

	while (f)
	{
		priority_queue<P> que;

		fill(dist, dist + V, INF);

		dist[s] = ms.d = 0;
		ms.num = s;
		que.push(ms);

		while (!que.empty())
		{
			m = que.top();
			que.pop();

			if (dist[m.num] == m.d)
			{
				int deg = G[m.num].size();
				for (int i = 0; i < deg; i++)
				{
					Edge& e = G[m.num][i];
					if (e.cap)
					{
						ms.num = e.to;
						ms.d = m.d + e.cost + h[m.num] - h[ms.num];
						if (ms.d < dist[ms.num])
						{
							dist[ms.num] = ms.d;
							prevv[ms.num] = m.num;
							preve[ms.num] = i;
							que.push(ms);
						}
					}
				}
			}
		}

		if (dist[t] == INF)
		{
			return -1;
		}

		for (int v = 0; v < V; v++)
		{
			h[v] += dist[v];
		}

		int d = f;
		for (int v = t; v != s; v = prevv[v])
		{
			d = _min(d, G[prevv[v]][preve[v]].cap);
		}
		f -= d;

		res += d * h[t];

		for (int v = t; v != s; v = prevv[v])
		{
			Edge& e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}
	return res;
}

int _min(const int& a, const int& b)
{
	return a < b ? a : b;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章