1. 程式人生 > >動態規劃特訓:切蛋糕(UVA1629)

動態規劃特訓:切蛋糕(UVA1629)

解題思路:利用動態規劃的思想,這裡的狀態即是蛋糕的上線、下線、左線、右線。然後進行記憶化搜素。遍歷豎著切的每一種方式和橫著切的每一種方式最終得到結果。狀態轉移方程稍微有些繁瑣,在這裡就不列舉了,可以直接看程式碼理解。

題目大意:給定一個方形蛋糕,上面有一些櫻桃,現在可以對該蛋糕做切割處理,保證切出來的每一份小蛋糕上都有一個櫻桃,且切割線的總長度最小。

Sample Input 3 4 3 1 2 2 3 3 2 Sample Output Case 1: 5

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int inf = 1 << 30;

int n, m, k, dp[51][51][51][51]; //蛋糕尺寸和櫻桃數目,以及dp數值
bool mark[51][51];  //為true即表示該位置放有櫻桃

int sum(int s, int x, int z, int y)  //此函式用來統計尺寸蛋糕內櫻桃的數目
{
	int ret = 0;
	for (int i = x; i <= s; i++)
	{
		for (int j = z; j <= y; j++)
		{
			if (mark[i][j] == true) ret++;
		}
	}
	return ret;
}

int f(int s, int x, int z, int y)
{
	int &ans = dp[s][x][z][y];
	if (ans != -1) return ans;
	int cherry = sum(s, x, z, y);
	if (cherry == 1) return ans = 0;   //如果尺寸內只有一個櫻桃,則無需切割,直接返回0
	ans = inf;
	for (int i = x; i <s; i++)
	{
		if (sum(i, x, z, y) == 0 || sum(s, i + 1, z, y) == 0) continue;  //若選擇的切割方式切不出櫻桃,則放棄
		ans = min(ans, f(i, x, z, y) + f(s, i + 1, z, y) + y - z+1);
	}
	for (int i = z;i < y; i++)
	{
		if (sum(s, x, z, i) == 0 || sum(s, x, i + 1, y) == 0) continue;  //同理,切出的蛋糕無櫻桃則放棄
		ans = min(ans, f(s, x, z, i) + f(s, x, i + 1, y) + s - x+1);
	}
	return ans;
}

int main()
{
	while (cin >> n >> m)
	{
		cin >> k;
		memset(mark, false, sizeof(mark));
		memset(dp, -1, sizeof(dp));
		for (int i = 1; i <= k; i++)
		{
			int x, y; cin >> x >> y;
			mark[x][y] = true;
		}
		cout << f(n, 1, 1, m) << endl;
	}
	return 0;
}