1. 程式人生 > >HDU 1455 Sticks(分組)

HDU 1455 Sticks(分組)

題目:

把陣列分成一些組,使得每個組的和都是r,求r的最小值

Description

George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero. 

Input

The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero. 

Output

The output file contains the smallest possible length of original sticks, one per line. 

Sample Input

9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0

Sample Output

6 5 這個題目主要是自己思路不清晰。

最開始提交的時候寫的遞迴函式居然只有2個引數,後來引數越來越多。。。

直到最後有6個引數才對。

昨天(7.22)最後的程式碼是這樣的

#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;

bool place(int *list,bool *b,int summ,int su,int n)
{ if (su == 0) { bool flag = true; for (int i = 0; i < n; i++)if (b[i])flag = false; if (flag)return true; return place(list, b, summ, summ, n); } if (su == summ) { int i = 0; while (!b[i])i++; if (i == n)return true; su -= list[i]; b[i] = false; if (place(list, b, summ, su, n))return true; b[i] = true; su += list[i]; return false; } for (int i = 0; i < n; i++) { if (b[i] && list[i] <= su) { su -= list[i]; b[i] = false; if (place(list, b, summ, su, n))return true; b[i] = true; su += list[i]; } } return false; } int main() { int max, sum; int n; while (cin >> n) { if (n == 0)break; int *list = new int[n]; bool *b = new bool[n]; for (int i = 0; i < n; i++)b[i] = true; //true表示可訪問,也就是還沒取走 max = 0; sum = 0; for (int i = 0; i < n; i++) { cin >> list[i]; if (max < list[i])max = list[i]; sum += list[i]; } sort(list, list + n,greater<int>()); //降序排列 for (int i = max; i <= sum; i++) { if (sum % i)continue; if (place(list, b, i, i, n)) { cout << i << endl; break; } } } return 0; }

到晚上看到別人15ms甚至0ms就執行結束,自己的程式碼卻超時,感覺很震驚。

感覺會有2種情況,要不是某個測試用例讓我的程式碼陷入了死迴圈,要不就是哪個地方寫的不對,造成了非常恐怖的重複無效運算。

到了晚上1點半,終於想通了,果然是有太多重複了。

place函式的主體是

for (int i = 0; i < n; i++)
	{
		if (b[i] && list[i] <= su)
		{
			su -= list[i];
			b[i] = false;
			if (place(list, b, summ, su, n))return true;
			b[i] = true;
			su += list[i];
		}
	}
這樣,每次這個i都是可以任選1個數的,相當於把組合的問題變成了排列了,於是就造成了階乘級別的重複計算,難怪超時。

正確程式碼:

#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;

bool place(int *list,bool *b,int summ,int su,int n,int start)
{
	if (su == 0)return place(list, b, summ, summ, n, 0);
	if (su == summ)
	{
		int i = 0;
		while (i < n && !b[i])i++;		//找到第一個還沒有分組的數
		if (i == n)return true;
		su -= list[i];
		b[i] = false;
		if (place(list, b, summ, su, n, i))return true;
		b[i] = true;
		su += list[i];
		return false;
	}
	if (start == n)return false;
	for (int i = start; i < n; i++)
	{
		if (b[i] && list[i] <= su)
		{
			su -= list[i];
			b[i] = false;
			if (place(list, b, summ, su, n, i + 1))return true;			//最後一個引數是不會超時的關鍵
			b[i] = true;
			su += list[i];
		}
	}
	return false;
}

int main()
{
	int max, sum;
	int n;
	while (cin >> n)
	{
		if (n == 0)break;
		int *list = new int[n];
		bool *b = new bool[n];
		for (int i = 0; i < n; i++)b[i] = true;	//true表示可訪問,也就是還沒取走
		max = 0;
		sum = 0;
		for (int i = 0; i < n; i++)		//輸入並求出max和sum
		{
			cin >> list[i];
			if (max < list[i])max = list[i];
			sum += list[i];
		}
		sort(list, list + n,greater<int>());	//降序排列
		for (int i = max; i <= sum; i++)
		{
			if (sum % i)continue;				//重要
			if (place(list, b, i, i, n, 0))
			{
				cout << i << endl;
				break;
			}
		}
	}
	return 0;
}