uva 317 Hexagon

题目地址:

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=253

题目描述:

 Hexagon 

Consider a game board consisting of 19 hexagonal fields, as shown in the figure below. We can easily distinguish three main directions in the shape of the board: from top to bottom, from top-left to bottom-right, and from top-right to bottom-left. For each of these primary directions, the board can be viewed as a series of rows, consisting of 3, 4, 5, 4, and 3 fields, respectively.

The game board has to be completely covered using a set of hexagonal pieces. Each piece carries three numbers, one for every primary board direction. Only three different numbers are used for each direction. Every possible combination of three numbers for all three directions is assigned to a piece, leading to a set of 27 unique pieces. (The board in the above figure is still in the process of being covered.)

The score of a board is calculated as the sum of all 15 row scores (5 rows for each primary direction). The row scores are calculated as follows: if all pieces in a row carry the same number for the direction of the row, the row score is this number multiplied by the number of pieces in the row. Otherwise (the pieces carry different numbers in the row direction) the row score is zero. Note that the pieces may not be rotated. For example, the score of the leftmost row in the figure is  , the score of the row to its right is .

While in the real game the pieces are chosen randomly and the set of pieces is fixed, we are interested in the highest possible score for a given set of numbers for each direction, when all pieces in a row carry the same number for the direction of the row. This means you have to choose those 19 pieces that result in the highest score.

Input

The first line of the input file contains an integer n which indicates the number of test cases. Each test case consists of three lines containing three integers each. Each of these three line contains the numbers for a single primary direction. From these numbers the set of pieces is generated.

Output

For each test case output a line containing the number of the case (`Test #1', `Test #2', etc.), followed by a line containing the highest possible score for the given numbers. Add a blank line after each test case.

Sample Input

1
9 4 3
8 5 2
7 6 1

Sample Output

Test #1
308
题意:

给出3组数,每组数有3个,每组数只能填一个基本方向(上下,左斜上,右斜下),分别冲3组数中各取一个数,组成一个小六边形,对角的数字相同且同一组的数字,让你用最多组成的27个小六边形,挑19个不重复的出来,组成一个大六边形,使得目标值最大,目标值等于3个基本方向上,每个列的数的综合,有一列的数不相同的,则该列权值为0。

题解:

三种方法:1、DFS+剪枝(TLE,可用于规律发现)2、规律推导(下标的规律组合 全排)3、规律推导(各个方向上的数量有规律 组合全排)

一、DFS+剪枝,以选中的小6变形为对象,当DFS到19时 停止搜索,计算目标值。这个表示剪了n久的枝还是超时,也想不出实质性的关键剪枝,都是些无关痛痒的剪枝,但是可以利用这个程序观察最大值得元素组成和分布的规律,后来在看到网上关于规律发现的总结才过掉,其规律总结为:

三个方向上的构成只可能是以下这一种可能性:一个方向上三个数分别乘以5(=5),6(=3+3),8(=4+4);另外两个方向上三个数分别乘以5(=5),7(=3+4),7(=3+4)

二、规律推导,将问题转型为:为了让每列值中都是相同的,所以我们可以这样看待问题,即在第一组数中选择数字填入5个容器中(5列,每列数量分别为3 4 5 4 3),这样有3个方向,所以有3组有5个容器的二维序列,这样我们只要求出这个二维序列代表值的总和就是所谓的目标值了。规律参考了网上:将输入的每组值,按组内从小到大排列,然后利用一、DFS+剪枝的标准程序,生成一定的样例,输出选中输入数的相应下标组成的二维矩阵,可以发现最大值的组成都是有固定的几组下标{1,1,0,2,2},{1,2,0,1,2},{1,2,0,2,1},{2,1,0,1,2},{2,1,0,2,1},{2,2,0,1,1}组成。

下面给出输入样例得到的输出:

308
1 0 2 0 1
0 0 2 1 1
0 1 2 0 1

308
1 0 2 0 1
0 0 2 1 1
1 0 2 1 0

308
1 0 2 0 1
0 1 2 0 1
0 0 2 1 1

308
1 0 2 0 1
0 1 2 0 1
1 1 2 0 0

308
1 0 2 0 1
1 0 2 1 0
0 0 2 1 1

308
1 0 2 0 1
1 0 2 1 0
1 1 2 0 0

308
1 0 2 0 1
1 1 2 0 0
0 1 2 0 1

308
1 0 2 0 1
1 1 2 0 0
1 0 2 1 0

第一行5个数表示上下方向的5列 (3 4 5 4 3) 分别用的是第一组中的哪些数值的下标,根据下标值可以索引到第一组数的值,后两行亦然;可以看到组成最大值308都是由这几组下标来组成的{1,1,0,2,2},{1,2,0,1,2},{1,2,0,2,1},{2,1,0,1,2},{2,1,0,2,1},{2,2,0,1,1}。所以我们将这几组下标值全排成3*5的矩阵序列,得到所有的组成情况(所有的矩阵情况),然后利用矩阵序列的下标索引到输入数值,然后对每种情况求和,求所有情况中最大的即可。

三、规律推导,同理,将问题转换成填充一个3*5的二维矩阵上,即如何从输入数中选择数值填入矩阵中,使其和最大。这次还是通过一、程序找规律,这次我们把矩阵单元中的下标换成输入的具体数值,下面将输出的结果打印供分析规律:

308
4 9 3 9 4
8 5 2 8 5
7 7 1 6 6
308
4 9 3 9 4
5 8 2 5 8
7 7 1 6 6
308
4 9 3 9 4
5 5 2 8 8
7 6 1 7 6
308
4 9 3 9 4
8 8 2 5 5
7 6 1 7 6
308
4 9 3 9 4
5 5 2 8 8
6 7 1 6 7
308
4 9 3 9 4
8 8 2 5 5
6 7 1 6 7
308
4 9 3 9 4
8 5 2 8 5
6 6 1 7 7
308
4 9 3 9 4
5 8 2 5 8
6 6 1 7 7

输入情况为题目中的样例输入。可以看到对于第一行其分布规律为{5,6,8}表示每组数中每个数用到的数量状况:5表示最小的数填中间,而根据3 4 5 4 3,中间列元素有5个,所以最小的数有5个;7表示第二小的数填第一列和最后一列数量加起来为3+3=6;8表示最大的数填到第二列和第四列,这样数量加起来为4+4=8。然后看第二行的分布规律是{5,7,7}:意义同上。第三行的分布规律仍然是{5,7,7}:意义同上。这样总结出这样一个规律:对于一个3*5的矩阵,它的其中一行(不一定是第一行,第二行,第三行也有可能)是{5,7,6}的分布,而剩余两行是{5,7,7}的分布规律。所以分布规律可以组合成以下3个矩阵(全排矩阵):

5 6 8   5 7 7   5 7 7
5 7 7   5 6 8   5 7 7
5 7 7   5 7 7   5 6 8
然后用这3个分布规律的矩阵算出所有的情况的总和,从中取最大即可。


证明:关于上面两个的证明,目前没想到什么方法去证明这个规律定理,怎么证呢??? 纠结。。。。。

代码:

一、DFS+剪枝(TLE)

/*
matrix:
0 0 x 0 0
0 x 0 x 0
x 0 x 0 x
0 x 0 x 0
x 0 x 0 x
0 x 0 x 0+
x 0 x 0 x
0 x 0 x 0
0 0 x 0 0

choose the DFS's depth or object is very important
hwo to prune this DFS????????
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <iostream>
using namespace std;
typedef struct pie
{
	/* data */
	int tb;//top-bottom
	int lr;//top left-bottom right
	int rl;//top right-bottom left
	pie()
	{
		tb=0;
		lr=0;
		rl=0;
	}
}pie,*pielink;
pie pieset[27+5];//3*3*3=27,begin with 1,pieset[0] is no initialzed piece
bool vis[27+5]={false};//the pie set of the vis array,begin with 1
int num[3][3]={0};//input num array
int cases=0;//the test case number of the input
int graph[9][5];//the graph of the case.the place cast trans to this graph or matrix please look at the top of this source,the value is the index of the pieset
int sum=0;//the score sum
int maxsum=0;//the max of the sums
int partsum=0;//use for prune
int partmaxsum=0;//use for prune
int sumstack[19+5]={0};//record the per DFS per sum,assure that per DFS sum >maxsum then this sum at end must be the true maxsum,begin with 1
int maxsumstack[19+5]={0};
bool graphvis[9][5]=
{
{false,false,true,false,false},
{false,true,false,true,false},
{true,false,true,false,true},
{false,true,false,true,false},
{true,false,true,false,true},
{false,true,false,true,false},
{true,false,true,false,true},
{false,true,false,true,false},
{false,false,true,false,false}
};
int ablecoor[19][2]=//the avaliable coordinate record
{
{2,0},{4,0},{6,0},
{1,1},{3,1},{5,1},{7,1},
{0,2},{2,2},{4,2},{6,2},{8,2},
{1,3},{3,3},{5,3},{7,3},
{2,4},{4,4},{6,4}
};
/*compare for the sort*/
bool cmp(int a,int b)
{
	return(a>b);
}
/*compare for the sort pieset*/
bool cmppie(pie a,pie b)
{
	return((a.tb+a.lr+a.rl)>(b.tb+b.lr+b.rl));
}
/*calculate the sum score,the  IsCheck has check the a row =0 's case,so we need not think about it*/
int CalcPie()
{
	int sum=0;
	//top - bottom
	sum+=pieset[graph[2][0]].tb+pieset[graph[4][0]].tb+pieset[graph[6][0]].tb;//3
	sum+=pieset[graph[1][1]].tb+pieset[graph[3][1]].tb+pieset[graph[5][1]].tb+pieset[graph[7][1]].tb;//4
	sum+=pieset[graph[0][2]].tb+pieset[graph[2][2]].tb+pieset[graph[4][2]].tb+pieset[graph[6][2]].tb+pieset[graph[8][2]].tb;//5
	sum+=pieset[graph[1][3]].tb+pieset[graph[3][3]].tb+pieset[graph[5][3]].tb+pieset[graph[7][3]].tb;//4
	sum+=pieset[graph[2][4]].tb+pieset[graph[4][4]].tb+pieset[graph[6][4]].tb;//3
	//topleft-bottomright
	sum+=pieset[graph[0][2]].lr+pieset[graph[1][3]].lr+pieset[graph[2][4]].lr;
	sum+=pieset[graph[1][1]].lr+pieset[graph[2][2]].lr+pieset[graph[3][3]].lr+pieset[graph[4][4]].lr;
	sum+=pieset[graph[2][0]].lr+pieset[graph[3][1]].lr+pieset[graph[4][2]].lr+pieset[graph[5][3]].lr+pieset[graph[6][4]].lr;
	sum+=pieset[graph[4][0]].lr+pieset[graph[5][1]].lr+pieset[graph[6][2]].lr+pieset[graph[7][3]].lr;
	sum+=pieset[graph[6][0]].lr+pieset[graph[7][1]].lr+pieset[graph[8][2]].lr;
	//topright-bottomleft
	sum+=pieset[graph[0][2]].rl+pieset[graph[1][1]].rl+pieset[graph[2][0]].rl;
	sum+=pieset[graph[1][3]].rl+pieset[graph[2][2]].rl+pieset[graph[3][1]].rl+pieset[graph[4][0]].rl;
	sum+=pieset[graph[2][4]].rl+pieset[graph[3][3]].rl+pieset[graph[4][2]].rl+pieset[graph[5][1]].rl+pieset[graph[6][0]].rl;
	sum+=pieset[graph[4][4]].rl+pieset[graph[5][3]].rl+pieset[graph[6][2]].rl+pieset[graph[7][1]].rl;
	sum+=pieset[graph[6][4]].rl+pieset[graph[7][3]].rl+pieset[graph[8][2]].rl;
	return(sum);
}
/*check the point that his neighbour 's  the direction value is equal*/
bool IsCheck(int i,int j,int k)
{
	int nexti=0,nextj=0,nextk=0;//note that ths place is sorted so we do not think about bottom, topright , bottomright
	//top - bottom next
	//top
	nexti=i-1;
	nextj=j;
	nextk=0;
	while(nexti>=0)
	{
		if(graphvis[nexti][nextj])
		{
			if(graph[nexti][nextj]>=0)
			{
				nextk=graph[nexti][nextj];
				if(pieset[k].tb!=pieset[nextk].tb)
				{
					return(false);
				}
			}
			else
			{
				break;//the DFS's path is sorted direction
			}
		}
		nexti--;
	}
	//top left
	nexti=i-1;
	nextj=j-1;
	nextk=0;
	while(nexti>=0&&nextj>=0)
	{
		if(graphvis[nexti][nextj])
		{
			if(graph[nexti][nextj]>=0)
			{
				nextk=graph[nexti][nextj];
				if(pieset[k].lr!=pieset[nextk].lr)
				{
					return(false);
				}
			}
			else
			{
				break;//the DFS's path is sorted direction
			}
		}
		nexti--;
		nextj--;
	}
	//bottom left
	nexti=i+1;
	nextj=j-1;
	nextk=0;
	while(nexti<=9-1&&nextj>=0)
	{
		if(graphvis[nexti][nextj])
		{
			if(graph[nexti][nextj]>=0)
			{
				nextk=graph[nexti][nextj];
				if(pieset[k].rl!=pieset[nextk].rl)
				{
					return(false);
				}
			}
			else
			{
				break;//the DFS's path is sorted direction
			}
		}
		nexti++;
		nextj--;
	}
	return(true);
}
/*output the maze*/
int outputmaze()
{
	//top - bottom
	printf("%d %d %d %d %d\n",pieset[graph[2][0]].tb,pieset[graph[1][1]].tb,pieset[graph[0][2]].tb,pieset[graph[1][3]].tb,pieset[graph[2][4]].tb);
	//topleft - bottomright
	printf("%d %d %d %d %d\n",pieset[graph[0][2]].lr,pieset[graph[1][1]].lr,pieset[graph[2][0]].lr,pieset[graph[4][0]].lr,pieset[graph[6][0]].lr);
	//topright - botomleft
	printf("%d %d %d %d %d\n", pieset[graph[0][2]].rl,pieset[graph[1][3]].rl,pieset[graph[2][4]].rl,pieset[graph[4][4]].rl,pieset[graph[6][4]].rl);
	return(0);
}
/*DFS the graph,cur is the count of the used piece and coorind is the index of the ablecoor[]*/
int DFS(int cur,int coorind)
{
	if(cur>=19)
	{
		sum=CalcPie();
		if(sum==308)
		printf("%d\n",sum );
		if(sum==308)
		outputmaze();
		if(sum>maxsum)
		{
			maxsum=sum;//update the max sum
		}
	}
	else
	{
		int i=0;
		int nextm=ablecoor[coorind][0],nextn=ablecoor[coorind][1];//the  next coordinate
		//find the place position
		for(i=1;i<=27;i++)
		{
			if(!vis[i])
			{
				if(IsCheck(nextm,nextn,i))
				{
					vis[i]=true;
					graph[nextm][nextn]=i;
					partsum+=pieset[i].tb+pieset[i].lr+pieset[i].rl;
					DFS(cur+1,coorind+1);
					partsum-=pieset[i].tb+pieset[i].lr+pieset[i].rl;
					graph[nextm][nextn]=0;
					vis[i]=false;
				}
			}
		}
	}
	return(0);
}
/*for test*/
int test()
{
	//test memset(-1),memset(-1) and memset(0) can use for the int array
	return(0);
}
/*main process*/
int MainProc()
{
	int t=0,i=0,j=0,k=0;
	scanf("%d",&cases);
	for(t=1;t<=cases;t++)
	{
		for(i=0;i<=2;i++)
		{
			for(j=0;j<=2;j++)
			{
				scanf("%d",&num[i][j]);
			}
			//sort(num[i],num[i]+3,cmp);//DFS prune ,sort the bit to small,place the bit to the front
		}
		//init
		for(i=0;i<=2;i++)//27 kinds of the piece,i map the first row of the num array,j map second ,k map third
		{
			for(j=0;j<=2;j++)
			{
				for(k=0;k<=2;k++)
				{
					//three dimension to the one dimension
					int ind=i+j*3+k*9+1;//begin with 1
					pieset[ind].tb=num[0][i];
					pieset[ind].lr=num[1][j];
					pieset[ind].rl=num[2][k];
				}
			}
		}
		//sort the pieset,according to the sum tb+lr+rl,DFS prune
		//sort(pieset+1,pieset+28,cmppie);//sort(pieset[1],pieset[27],cmppie); is error written,begin with 1
		memset(vis,false,sizeof(vis));
		memset(graph,0,sizeof(graph));
		memset(sumstack,0,sizeof(sumstack));
		memset(maxsumstack,0,sizeof(maxsumstack));
		sum=0;
		maxsum=0;
		partsum=0;
		partmaxsum=0;
		DFS(0,0);//cur is the count of the pieset cur<=19
		printf("Test #%d\n%d\n\n",t ,maxsum);
	}
	return(0);
}
int main(int argc, char const *argv[])
{
	/* code */
	MainProc();
	return 0;
}

二、规律推导(下标矩阵全排)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <iostream>
#include <algorithm>
using namespace std;
int pie[3][3]={0};//the input number
int indexpie[6][6]=
{
  {1,1,0,2,2},{1,2,0,1,2},{1,2,0,2,1},{2,1,0,1,2},{2,1,0,2,1},{2,2,0,1,1}
};//the value is the index of the pie array
int rowcol[5]={3,4,5,4,3};
int maxs=0;//the max of the sum
int sum=0;//the now sum of the score board
int cases=0;//the number of the test case
/*for test*/
int test()
{
	return(0);
}
/*main process*/
int MainProc()
{
	int i=0,j=0,k=0,t=0,h=0;
	scanf("%d",&cases);
	for(t=1;t<=cases;t++)
	{
		for(i=0;i<=3-1;i++)
		{
			for(j=0;j<=3-1;j++)
			{
				scanf("%d",&pie[i][j]);
			}
			sort(pie[i],pie[i]+3);//sort the value per row
		}
		//init
		sum=0;
		maxs=-1;
		//conbine the all cases for the indexpie
		for(i=0;i<=6-1;i++)
		{
			for(j=0;j<=6-1;j++)
			{
				for(k=0;k<=6-1;k++)
				{
					if(i==j||j==k||i==k)//no repeat
					{
						continue;
					}
					//get the value as the pie 's index
					sum=0;
					for(h=0;h<=6-1;h++)
					{
						sum+=(pie[0][indexpie[i][h]]+pie[1][indexpie[j][h]]+pie[2][indexpie[k][h]])*rowcol[h];
					}
					if(sum>maxs)
					{
						maxs=sum;
					}
				}
			}
		}
		printf("Test #%d\n%d\n\n",t,maxs );
	}
	return(0);
}
int main(int argc, char const *argv[])
{
	/* code */
	MainProc();
	return 0;
}

三、规律推导(数量矩阵全排)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
int num[3][3]={0};
int cnt[3][3][3]=//map to the sorted row 3 numbers
{
	{{5,6,8},{5,7,7},{5,7,7}},
	{{5,7,7},{5,6,8},{5,7,7}},
	{{5,7,7},{5,7,7},{5,6,8}}
};
int cases=0;//the count of the test
/*for test*/
int test()
{
	return(0);
}
/*main process*/
int MainProc()
{
	int t=0,i=0,j=0,k=0;
	scanf("%d",&cases);
	for(t=1;t<=cases;t++)
	{
		for(i=0;i<=2;i++)
		{
			for(j=0;j<=2;j++)
			{
				scanf("%d",&num[i][j]);
			}
			sort(num[i],num[i]+3);
		}
		int sum=0;
		int maxsum=0;
		for(k=0;k<=2;k++)//cnt[0] map to num[k]
		{
			sum=0;
			for(i=0;i<=2;i++)
			{
				for(j=0;j<=2;j++)
				{
					sum+=num[i][j]*cnt[k][i][j];
				}
			}
			if(sum>maxsum)
			{
				maxsum=sum;
			}
		}
		printf("Test #%d\n%d\n\n",t,maxsum);
	}
	return(0);
}
int main(int argc, char const *argv[])
{
	/* code */
	MainProc();
	return 0;
}



你可能感兴趣的:(规律推导,DFS剪枝)