poj1069 & hdu1362The Bermuda Triangle(建坐标系+剪枝+dfs)

The Bermuda Triangle
Time Limit: 2000MS   Memory Limit: 32768K
Total Submissions: 1098   Accepted: 510

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

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
这题又跪了一整天,终于过了,内牛满面啊有木有!
一天一题的节奏给男神cms跪了啊有木有!
男神Orz   Orz!!
题目大意:看题目中的图片,一个边长为s的正六边形可以完全分成6*s*s个小正三角形,现在给定一个正六边形的边长,然后再给n种小正三角形,问能否用这n种小正三角形填满这个正六边形。小三角形可以不全用。每种小三角无限用。
题目分析:只要判断能否用小三角填满大六边形,而且数据范围不大,dfs + 回溯。不过这题麻烦在是三角形,不好表示,回溯的时候不好标记,所以考虑建立一个坐标系,将所有的点都一一标识出来。
poj1069 & hdu1362The Bermuda Triangle(建坐标系+剪枝+dfs)_第1张图片


大致就是这样的。其余的坐标请自行补全,这样的话,就可以用一个二维数组标记了。而且这样的坐标还是有规律的。后面可以用。还要记得加几个剪枝:
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;
}


你可能感兴趣的:(搜索,DFS)