1167

/*
题意就是找等差数列,最少等差数列个数

深度挖掘信息,这个等差数列必须满的。我所谓的满,是指这个数列不能在0到59之间在找一个了
而且必须有两个,那么第一个数值必须小于30了,否则等差小于30,然后就可以在之前在找一个了
通过这样,可以把所有的等差数列描述出来
为了搜索的更快,按照等差数列的大小从大到小排列
在搜索的过程中,及时找出下限,如果下限仍然大于最优解,那么就舍去

此题如果不加强剪枝,还真不好过。按照节点个数递减的顺序搜索,也是不错的选择。
*/

// include file
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <ctime>

#include <iostream>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <bitset>

#include <algorithm>
#include <string>
#include <vector>
#include <queue>
#include <set>
#include <list>
#include <functional>

using namespace std;

// typedef
typedef __int64 LL;

// 
#define read freopen("in.txt","r",stdin)
#define write freopen("out.txt","w",stdout)

#define Z(a,b) ((a)<<(b))
#define Y(a,b) ((a)>>(b))

const double eps = 1e-6;
const double INFf = 1e100;
const int INFi = 1000000000;
const LL INFll = (LL)1<<62;
const double Pi = acos(-1.0);

template<class T> inline T sqr(T a){return a*a;}
template<class T> inline T TMAX(T x,T y)
{
	if(x>y) return x;
	return y;
}
template<class T> inline T TMIN(T x,T y)
{
	if(x<y) return x;
	return y;
}
template<class T> inline T MMAX(T x,T y,T z)
{
	return TMAX(TMAX(x,y),z);
}
template<class T> inline T MMIN(T x,T y,T z)
{
	return TMIN(TMIN(x,y),z);
}
template<class T> inline void SWAP(T &x,T &y)
{
	T t = x;
	x = y;
	y = t;
}


// code begin
int N;
int da[60];
int ans;
int remain;
struct node
{
	int first;
	int interval;
	int times;
	friend bool operator<(node a,node b)
	{
		return a.times>b.times;
	}
};
node seg[1000];
int segdx;

int check(int s,int inc)
{
	if(s+inc>=60) return 0;
	int cnt = 0;
	for(int i=s;i<60;i+=inc)
	{
		if(!da[i]) return 0;
		cnt++;
	}
	return cnt;
}

void update(int s,int inc,int dx)
{
	for(int i=s;i<60;i+=inc)
	{
		da[i]+=dx;
	}
}

void dfs(int i,int curt) //可行数列的第几个了,当前的最优解
{
	if(curt>ans) return ;
	if(!remain)
	{
		if(curt<ans)
			ans=curt;
		return;
	}
	//printf("%d\n",i);
	// 粗略估计一下,剩下最少能用多少个数列
	int up,j=i;
	for(;j<segdx;j++) //找到一个比remain小的times的数列开始
	{
		if(seg[j].times<=remain)
		{
			break;
		}
	}

	for(;j<segdx;j++)
	{
		up = curt+1+(remain-seg[j].times)/seg[j].times; //求下限 ,这是一个相等重要的剪枝,要深刻理解其内涵
		if( up>=ans) return;
		if( check(seg[j].first,seg[j].interval) )
		{
			//printf("%d %d here\n",seg[j].first,seg[j].interval);
			remain -= seg[j].times;
			update(seg[j].first,seg[j].interval,-1);
			if(da[seg[j].first]) dfs( j,curt+1); //此处只所以还是从j开始,是因为j还能满足条件,因为有重复的时间了
			else dfs(j+1,curt+1);
			update(seg[j].first,seg[j].interval,1);
			remain += seg[j].times;
		}
	}
}

int main()
{
	read;
	write;
	int tmp;
	while(scanf("%d",&N)==1)
	{
		memset(da,0,sizeof(da));
		remain = N;
		for(int i=0;i<N;i++)
		{
			scanf("%d",&tmp);
			da[tmp]++;
		}
		segdx = 0;
		for(int i=0;i<30;i++)
		{
			for(int j=i+1;j<60-i;j++)
			{
				if((tmp=check(i,j)))
				{
					seg[segdx].first = i;
					seg[segdx].interval = j;
					seg[segdx].times = tmp;
					segdx++;
				}
			}
		}
		sort(seg,seg+segdx);
		ans = 17;
		remain = N;
		dfs(0,0);
		printf("%d\n",ans);
	}
	return 0;
}

posted @ 2011-04-22 10:24  AC2012  阅读(180)  评论(0编辑  收藏  举报