POJ 1020

题目大意不多说了,主要说点题目中应该注意的问题。
1.如何放:基本就是大家所说的dfs,注意减枝即可;
2.怎么放才是正确的 : 每次选择放得最少的列,在其基础上放置对应的正方形(注意减枝)。

该题数据很弱,即你按照行搜索,优先放置列上剩余最少的(如左图),也可以AC,正确做法是横向放置,不然在dfs时容易出现右图的问题。ps:由小到大也会存在这个问题的,只不过显示的图片不一样。
图片一图片二

正确做法是选取剩余最多的列放置正方形,而不是单纯按列排满,如下图所示,就绝对不会出现中间留有空白的情况。
图片三
正常搜索会出现超时的情况

bool dfs(int x)
{
    total_times++;
    int result = 0, left = 0;
    for (int i = 9; i >= 0; i--)
    {
        if (input[i])
        {
            left = 1;//有剩余数据则表示好需要搜索
            if (set(x, i + 1)) // 设置对应的列数据,失败时能还原现场
            {
                int next_x = maxLeft();//获取下个最大的列
                input[i] --;
                result = dfs(next_x);
                if (result)
                {
                    return true;
                }
                else
                {
                    input[i] ++;
                    clearLast(x, i + 1);//还原现场
                }
            }
        }
    }
    if (left == 0)
    {
        return true;
    }
    return result;
}

这里最大的问题就是未对set(x, i + 1)进行减枝,导致dfs失败时不停调用,出现tle。其实在确定下一个dfs的列序号时,就能获取到其最大能放入的宽度。具体看代码(79MS),应该还有优化空间。如果有疑问,或者代码中存在问题,欢迎一起交流

#include<iostream>
#include<string>
#include <iomanip>
#include<math.h>
#include <fstream>  
using namespace std;

int input[10] = {};
int data[10] = { 1, 4, 9, 16, 25, 36, 49, 64, 81, 100 };
int a[40] = {};
int size;
int total_times = 0;

//置位
bool set(int x, int length)
{
    if (x + length > size)
    {
        return false;
    }
    int index = x, error = 1;
    for (; index < length + x; index++)
    {
        a[index] += length;
        if (a[index]> size)
        {
            error = 0;
            break;
        }
    }
    if (!error)
    {
        //还原现场
        for (int i = x; i <= index; i++)
        {
            a[i] -= length;
        }
    }
    return error;
}

//选占用最短的x,顺便获取支持最长长度
void maxLeft(int &x, int &maxLength)
{
    x = 0;
    for (int i = 1; i < size; i++)
    {
            if (a[x] > a[i])
                x = i;
    }
    maxLength = 0;
    for (int i = x; i < size; i++)
    {
        if (a[x] == a[i])
        {
            maxLength ++;
        }
        else
        {
            break;
        }
    }
}

//清楚上次操作
void clearLast(int x, int length)
{
    for (int i = x; i < x + length; i++)
    {
        a[i] -= length;
    }
}

//x为选择的列,maxLength为选择该列时最大支持的正方形大小
bool dfs(int x, int maxLength)
{
    total_times++;
    int result = 0, left = 0;
    for (int i = 9; i >= 0; i--)
    {
        if (input[i])
        {
            //正方形放完即认为结束,否则继续dfs
            left = 1;
            if (maxLength >= i && set(x, i + 1))  //maxLength这里用于减枝,不然会tle
            {
                int next_x, next_maxLength;
                maxLeft(next_x, next_maxLength);
                input[i] --;
                result = dfs(next_x, next_maxLength);
                if (result)
                {
                    return true;
                }
                else
                {
                    //还原现场
                    input[i] ++;
                    clearLast(x, i + 1);
                }
            }
        }
    }
    if (left == 0)
    {
        return true;
    }
    return result;
}

int main()
{
    //ifstream in("d:\\document\\test.txt");
    int max_case = 0, temp_value, cur_case = 0;
    cin >> max_case;
    getchar();
    while (cur_case < max_case)
    {
        total_times = 0;
        cur_case++;
        int count = 0;
        int max_size = 0, max_value = 0, temp = 0;
        memset(input, 0, 10 * sizeof(int));
        memset(a, 0, 40 * sizeof(int));
        cin >> size >> count;
        for (int i = 0; i < count; i++)
        {
            cin >> temp;
            max_size += data[temp - 1];
            input[temp - 1] ++;
        }
        if (max_size != size * size)
        {
            cout << "HUTUTU!" << endl;
        }
        else
        {
            if (dfs(0, 10))
            {
                cout << "KHOOOOB!" << endl;
            }
            else
            {
                cout << "HUTUTU!" << endl;
            }
        }
    }
    system("pause");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值