HDU-3506-Monkey Party

HDU-3506-Monkey Party

传送门

经典区间dp拓展。
在石子合并基础上化曲为直即可。

题目大意:围成一个圆形的一堆猴子要相互认识,问他们相互认识的最小代价。其中两猴子相识的代价等于他们各自相互认识的猴子的代价总和。如果两个猴子相识那么代表着他们原先各自的朋友也相互相识。这个代价就相当于石子合并的时候各个堆的个数。代价。一样的道理

然后这个地方把圆形化为一条直线。我们直接把1~n-1的那些猴子补到n的后面就可以了。长度为2n-1。
最后维护答案就是维护长度为n的dp就可以啦。
在计算前缀和的时候我们把1~2
n-1都计算一遍。
数据输入的时候记得保存一下最后移到后面的那些值。方便后面求和。有些数重复计算了不要紧。反正最后求得是区间和。总要减的。

石子合并那道题我说的很清楚啦~
这里就不多说啦~

代码部分:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
const int INF = 1e9 + 10;

int a[N];
int dp[N][N];
int s[N][N];
int sum[N];

int main()
{
	int n;
	while (~scanf ("%d", &n))
	{
		for (int i = 1; i <= n; i++)
		{
			scanf ("%d", &a[i]);
			sum[i] = sum[i - 1] + a[i];
			a[i + n] = a[i];
		}
		for (int i = n + 1; i < 2 * n; i++)
		{
			sum[i] = sum[i - 1] + a[i];
		}
		n = 2 * n - 1;
		for (int i = 1; i <= n; i++)
		{
			dp[i][i] = 0;
			s[i][i] = i;
		}
		for (int len = 1; len < n; len++)
		{
			for (int i = 1; i <= n - len; i++)
			{
				int j = i + len;
				dp[i][j] = INF;
				for (int k = s[i][j - 1]; k <= s[i + 1][j]; k++)
				{
					if (dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1] < dp[i][j])
					{
						dp[i][j] = dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1];
						s[i][j] = k;
					}
				}
			}
		}
		n = (n + 1) >> 1;
		int ans = INF;
		for (int i = 1; i <= n; i++)
		{
			ans = min(ans, dp[i][n + i - 1]);
		}
		cout << ans << endl;
	}
	return 0;
}
发布了112 篇原创文章 · 获赞 3 · 访问量 2644

猜你喜欢

转载自blog.csdn.net/qq_44624316/article/details/104895701