1069:The Bermuda Triangle

描述

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).

居住在百慕达三角隐蔽区域的人们把他们需要的所有东西都做成三角形。一天,有人决定打破常规,烤一个六边形的蛋糕。但和往常一样,他必须把蛋糕切成三角形。这些棋子是等边三角形,但是尺寸不同,适合不同的人。他可以根据需要用尽可能多的三角形把蛋糕切成小块,这样蛋糕上就什么都不剩了。例如,下图显示了一种方法,一个六边形侧9可以切成三角形侧2和3。(蛋糕沿着粗线切,用细线表示大小)。


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.

输入是六边形和三角形类型(由它们的边长指定) ,目标是确定六边形是否可以被给定的三角形类型完全划分。

输入
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).

输入文件的第一行包含一个单一的整数 t (1 < = t < = 10) ,即测试用例的数量,然后是每个测试用例的输入数据。每个测试用例由一条线组成,其中包含 s (1 < = s < = 25) ,六边形的边长,然后是 n,三角形类型的数量(1 < = n < = 10) ,然后是 n 个整数,表示每个三角形类型的边长(包括1和25)。

输出
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.

每个测试用例应该有一个包含 YES 或 NO 的输出行,这取决于六边形是否可以被给定的三角形类型完全划分。

样例输入
3
5 2 2 3
7 2 3 2
13 2 2 3
样例输出
NO
NO
YES
来源
Tehran 2001


分析

只要判断能否用小三角填满大六边形,而且数据范围不大,dfs + 回溯。不过这题麻烦在是三角形,不好表示,回溯的时候不好标记,所以考虑建立一个坐标系,将所有的点都一一标识出来。

其余的坐标请自行补全,这样的话,就可以用一个二维数组标记了。而且这样的坐标还是有规律的。后面可以用。还要记得加几个剪枝:
1:如果有小三角形的边长能整除六边形边长,直接yes。
2:如果给的小三角形中某一个边长能整除另一个边长,去掉大的。
3:如果给的n个小三角形的边长不能组合成六边形边长,直接no。
4:依次搜六边形的1/6,1/3,1/2,1/1。期中任意一个能组成,直接yes。
5:三角形从小往大搜,当某个三角形不能放的时候,直接return。


代码

代码有亿点长,没耐心看的话直接复制

#include <iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
const int N = 101;
int lcm[30];
bool map[N][N];
int border[4][30][2];
int border4[55][2];
int n,s;
bool flag;
 
void getborder()
{
    memset(border,0,sizeof(border));
    memset(border4,0,sizeof(border4));
    int i,j;
    int ss = s * 2;
    border[1][1][1] = ss - 1;
    border[1][1][0] = 1;
    for(i = 2;i <= s;i ++)
    {
        border[1][i][0] = 1;//类型1的第i行左边界为1
        border[1][i][1] = border[1][i - 1][1] - 2;
    }
    for(i = 1;i <= s;i ++)
    {
        border[2][i][0] = 1;
        border[2][i][1] = ss;
    }
    border[3][1][0] = 1;
    border[3][1][1] = ss * 2 - 1;
    for(i = 2;i <= s;i ++)
    {
        border[3][i][0] = 1;
        border[3][i][1] = border[3][i - 1][1] - 2;
    }
    border4[1][0] = ss;
    border4[1][1] = ss * 2;
    for(i = 2;i <= s;i ++)
    {
        border4[i][0] = border4[i - 1][0] - 2;
        border4[i][1] = border4[i - 1][1];
    }
    border4[s + 1][0] = 1;
    border4[s + 1][1] = ss * 2 - 1;
    for(i = s + 2;i <= ss;i ++)
    {
        border4[i][0] = 1;
        border4[i][1] = border4[i - 1][1] - 2;
    }
    //for(i = 1;i <= ss;i ++)
       // printf("row:%d  left:%d  right:%d\n",i,border4[i][0],border4[i][1]);
}
 
void predeal()
{
    int i,j,k;
    for(i = 1;i < n;i ++)
    {
        k = 0;
        for(j = i + 1;j <= n;j ++)
            if(lcm[j] % lcm[i] == 0)
            {
                lcm[j] = 73;
                k ++;
            }
        if(k)
            sort(lcm + 1,lcm + 1 + n);
        n -= k;
    }
}
 
void dfs(int cur,int len)
{
    if(flag)
        return ;
    if(len > s)
        return ;
    if(len == s)
    {
        flag = true;
        return;
    }
    for(int i = 1;i <= n;i ++)
        dfs(i,len + lcm[i]);
}
 
int nextint()
{
    char c;
    int ret = 0;
    while(isspace(c = getchar()))
        ;
    ret = c - '0';
    while((c = getchar()) >= '0' && c <= '9')
        ret = ret * 10 + c - '0';
    return ret;
}
 
bool canput1(int row,int col,int len)
{
    if(row + len - 1 > s + s)
        return false;
    int i,j;
    if(col & 1)
    {
        int dp = 0;
        for(i = row + len - 1;i >= row;i --)
        {
            for(j = col;j <= col + dp;j ++)
                if(map[i][j] || j > border4[i][1] || j < border4[i][0])
                    return false;
            dp += 2;
        }
    }
    else
    {
        int dp = 0;
        for(i = row;i <= row + len - 1;i ++)
        {
            for(j = col;j >= col - dp;j --)
                if(map[i][j] || j > border4[i][1] || j < border4[i][0])
                    return false;
            dp += 2;
        }
    }
    return true;
}
 
bool canput2(int row,int col,int len,int type)
{
    if(row + len - 1 > s)
        return false;
    int i,j;
    if(col & 1)
    {
        int dp = 0;
        for(i = row + len - 1;i >= row;i --)
        {
            for(j = col;j <= col + dp;j ++)
                if(map[i][j] || j > border[type][i][1] || j < border[type][i][0])
                    return false;
            dp += 2;
        }
    }
    else
    {
        int dp = 0;
        for(i = row;i <= row + len - 1;i ++)
        {
            for(j = col;j >= col - dp;j --)
                if(map[i][j] || j > border[type][i][1] || j < border[type][i][0])
                    return false;
            dp += 2;
        }
    }
    //printf("canput %d %d %d\n",row,col,len);
    return true;
}
 
void put(int row,int col,int len,int add)
{
    int i,j,dp;
    if(col & 1)
    {
        dp = 0;
        for(i = row + len - 1;i >= row;i --)
        {
            for(j = col;j <= col + dp;j ++)
                map[i][j] = add;
            dp += 2;
        }
    }
    else
    {
        dp = 0;
        for(i = row;i <= row + len - 1;i ++)
        {
            for(j = col - dp;j <= col;j ++)
                map[i][j] = add;
            dp += 2;
        }
    }
    //printf("put in %d %d len %d %d\n",row,col,len,add);
    //system("pause");
}
 
void Dfs(int x,int y,int type,int num)
{
    //printf("%d  %d\n",x,num);
    if(flag)
        return;
    int j,k;
    bool f = false;
    if(type < 4)
    {
        if(x >= s)
            return;
        for(j = y;j <= border[type][x][1];j ++)
        {
            if(!map[x][j])
            {
                f = true;
                for(k = 1;k <= n;k ++)
                {
                    if(canput2(x,j,lcm[k],type))
                    {
                        if(num == lcm[k] * lcm[k])
                        {
                            flag = 1;
                            return;
                        }
                        put(x,j,lcm[k],1);
                        int tmp = j;
                        if(j & 1)
                            tmp += (lcm[k] * 2 - 1);
                        else
                            tmp ++;
                        if(tmp > border[type][x][1])
                            Dfs(x + 1,1,type,num - lcm[k]*lcm[k]);
                        else
                            Dfs(x,tmp,type,num - lcm[k] * lcm[k]);
                        put(x,j,lcm[k],0);
                    }
                    else
                        return;
                }
            }
        }
        if(f == false)//如果第x行都被放了,去x+1行找
            Dfs(x + 1,1,type,num);
    }
    else
    {
        if(x >= s + s)//取等号是因为一定没有边长为1的三角形(已处理),上同
            return;
        for(j = y;j <= border4[x][1];j ++)
        {
            if(!map[x][j])
            {
                f = true;
                for(k = 1;k <= n;k ++)
                {
                    if(canput1(x,j,lcm[k]))
                    {
                        if(num == lcm[k] * lcm[k])
                        {
                            flag = 1;
                            return;
                        }
                        put(x,j,lcm[k],1);
                        int tmp = j;
                        if(j & 1)
                            tmp += (lcm[k] * 2 - 1);
                        else
                            tmp ++;
                        if(tmp > border4[x][1])
                            Dfs(x + 1,1,type,num - lcm[k] * lcm[k]);
                        else
                            Dfs(x,tmp,type,num - lcm[k] * lcm[k]);
                        put(x,j,lcm[k],0);
                    }
                    else
                        return;
                }
            }
        }
        if(f == false)
            Dfs(x + 1,1,type,num);
    }
}
 
int main()
{
    int i,j,k;
    int t;
    t = nextint();
    while(t --)
    {
        s = nextint();
        n = nextint();
        flag = false;
        for(i = 1;i <= n;i ++)
        {
            lcm[i] = nextint();
            if(lcm[i] > s)
            {
                i --;
                n --;
                continue;
            }
            if(s % lcm[i] == 0)
                flag = true;
        }
        if(flag)//剪枝1
        {
            printf("YES\n");
            continue;
        }
        sort(lcm + 1,lcm + 1 + n);//剪枝5
        predeal();//剪枝2
        flag = false;
        for(i = 1;i <= n;i ++)
            dfs(i,lcm[i]);//剪枝3
        if(flag == false)
        {
            printf("NO\n");
            continue;
        }
        flag = false;
        int num;
        getborder();
        for(i = 1;i <= 4 && flag == false;i ++)//剪枝4 i从1-4分别表示六边形的1/6,1/3,1/2,1/1
        {
            //printf("%d\n",i);
            memset(map,0,sizeof(map));
            if(i < 4)
                num = s * s * i;
            else
                num = s * s * 6;
            Dfs(1,1,i,num);//row,col,type
        }
        if(flag)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

给个赞和关注吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值