poj 1069 The Bermuda Triangle ( 强大的建系 )

The Bermuda Triangle
Time Limit: 2000MS   Memory Limit: 32768K
Total Submissions: 1125   Accepted: 531

Description

People in the hidden region of the Bermuda Triangle make everything they need in triangular shapes. One day, someone decided to break the rule and bake a hexagonally shaped cake. But as usual, he has to serve the cake in triangular pieces. The pieces are equilateral triangles but in different sizes for different people. He can use as many triangles as needed to cut the cake into pieces, such that nothing remains from the cake. For example, the following figure shows one way that a hexagon with side 9 can be cut into triangles with side 2 and 3. (The cake is cut along the thick lines, thin lines are drawn to show the sizes). 
poj 1069 The Bermuda Triangle ( 强大的建系 )_第1张图片
Input is a hexagon and triangle types (specified by the length of their sides) and the goal is to decide if the hexagon can be completely divided by the given triangle types.

Input

The first line of the input file contains a single integer t (1 <= t <= 10), the number of test cases, followed by the input data for each test case. Each test case consists of a single line, containing s (1 <= s <= 25), the length of the hexagon's side, followed by n, the number of triangle types (1 <= n <= 10), followed by n integers representing the length of each triangle type's side (between 1 and 25, inclusive).

Output

There should be one output line per test case containing either YES or NO depending on whether the hexagon can be completely divided by the given triangle types.

Sample Input

3
5 2 2 3
7 2 3 2
13 2 2 3

Sample Output

NO
NO
YES

Source

Tehran 2001


题意:现在给定一个正六边形的边长,然后再给n种小正三角形,问能否用这n种小正三角形填满这个正六边形。小三角形可以不全用。每种小三角无限用。

思路:这题关键是建系,我是以60度角 以六边形左上角的边“ / ” 和下面的 " _ " 这条边建的系  也可以以120度角来建系  看自己习惯哪一种    然后我输出一下我建系后六边形是怎样表示的 

边长为3的六边形:

000000011111

000000000111

000000000001

100000000000

111000000000

111110000000

注:(1)、每个0、1表示边长为一的小三角形   (六边形最基础的元素)

       (2)、等边三角形映射到这个左边中即为直角三角形了   如下图则是表示边长为3的三角形  (正着和倒着)

                      0                                     00000

                      000                                     000

                      00000                                     0


然后就只要注意一些预处理和标记取消标记就好dfs了   

预处理+剪枝

1:如果有小三角形的边长能整除六边形边长,直接yes。
2:如果给的小三角形中某一个边长能整除另一个边长,去掉大的。
3:三角形从小往大搜,当某个三角形不能放的时候,直接return。

感想:做这题时  一点思路都没有   又没有解题报告研究   后来看见 poj discuss 上面有AC代码  于是拿回来研究  
开始一点都不懂别人的思路   后来慢慢输出中间结果就懂了  花了一个下午研究+一个晚上写   终于AC了    还是蛮兴奋滴    奋斗

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 110
using namespace std;

int n,m,flag,cnt;
int a[15],b[15];
bool mp[maxn][maxn];

void showmap()                // 中间输出函数  方便debug
{
    int i,j;
    printf("\n");
    for(i=1; i<=2*n; i++)
    {
        for(j=1; j<=4*n; j++)
        {
            printf("%d",mp[i][j]);
        }
        printf("\n");
    }
}
void presolve()
{
    int i,j;
    sort(a+1,a+m+1);
    cnt=0;
    for(i=1; i<=m; i++)
    {
        if(a[i]>=2*n) break;
        for(j=1; j<=cnt; j++)
        {
            if(a[i]%b[j]==0) break;
        }
        if(j==cnt+1) b[++cnt]=a[i];
    }
}
void init()
{
    int i,j,temp;
    memset(mp,1,sizeof(mp));
    for(i=1; i<=n; i++)
    {
        temp=2*n+1+(i-1)*2;
        for(j=1; j<=temp; j++)
        {
            mp[i][j]=0;
        }
    }
    for(i=n+1; i<=2*n; i++)
    {
        for(j=4*n; j>1+(i-n-1)*2; j--)
        {
            mp[i][j]=0;
        }
    }
}
bool isok(int u,int v,int k)
{
    int i,j;
 //   printf("u:%d v:%d k:%d\n",u,v,k);
    if(v%2==0)
    {
        for(i=u+b[k]-1;i>=u;i--)
        {
            for(j=v+2*(b[k]-1);j>=v+2*(b[k]-1)-2*(b[k]+u-1-i);j--)
            {
                if(mp[i][j])
                {
  //                  printf("i:%d j:%d\n",i,j);
                    return false;
                }
            }
        }
    }
    else
    {
        for(i=u;i<u+b[k];i++)
        {
            for(j=v;j<v+1+2*(i-u);j++)
            {
                if(mp[i][j]) return false;
            }
        }
    }
    return true;
}
void mark(int u,int v,int k,int flag1)
{
    int i,j;
    if(v%2==0)
    {
        for(i=u+b[k]-1;i>=u;i--)
        {
            for(j=v+2*(b[k]-1);j>=v+2*(b[k]-1)-2*(b[k]+u-1-i);j--)
            {
                if(flag1) mp[i][j]=1;
                else mp[i][j]=0;
            }
        }
    }
    else
    {
        for(i=u;i<u+b[k];i++)
        {
            for(j=v;j<v+1+2*(i-u);j++)
            {
                if(flag1) mp[i][j]=1;
                else mp[i][j]=0;
            }
        }
    }
}
void dfs(int x,int y)
{
    int i,j;
    if(flag) return ;
 //   showmap();
 //   printf("x:%d y:%d\n",x,y);
    if(x==2*n&&y>4*n||x>2*n)
    {
        flag=1;
        return ;
    }
    if(y>4*n) dfs(x+1,1);
    else if(mp[x][y])             // 加上else  不然会死循环  想一想 为什么
    {
        for(j=y+1; j<=4*n; j++)
        {
            if(mp[x][j]==0) break;
        }
        dfs(x,j);
    }
    else
    {
        for(i=1; i<=cnt; i++)
        {
            if(isok(x,y,i))
            {
                mark(x,y,i,1);     // 标记
                dfs(x,y+1);
                mark(x,y,i,0);     // 取消标记
            }
            else break;
        }
    }
}
int main()
{
    int i,j,t;
    scanf("%d",&t);
    while(t--)
    {
        flag=0;
        scanf("%d%d",&n,&m);
        for(i=1; i<=m; i++)
        {
            scanf("%d",&a[i]);
            if(n%a[i]==0) flag=1;
        }
        if(flag)  printf("YES\n");      // 预处理  三角形的边长是六边形约数则一定满足条件
        else
        {
            presolve();                 // 预处理
            init();
            showmap();
            dfs(1,1);
            if(flag)  printf("YES\n");
            else printf("NO\n");
        }
    }
}





你可能感兴趣的:(poj 1069 The Bermuda Triangle ( 强大的建系 ))