HDU 1518 Square(DFS)

这题跟前几天做的搜索题相比,难一些,首先思路就不是那么好想

要判断一组stick(木棒)能否组成正方形,首先要明确一点,如果可以围成正方形,那么木棒的长度总和一定是4的倍数。这个道理很好理解,也可以作为一个剪枝条件。

自己按照dfs的写法也写了一个,给的测试数据都能过,但提交5000MS严重超时

#include<iostream>
#include<algorithm> 
using namespace std;

int sum;
bool flag = false;
int x[22];
int a[22];
int tag[22];

bool jianzhi(int i, int side)
{
	if(tag[i] == 1)				//所取的木棒不能已经取过 
		return false;
	if(sum + a[i] > side)		//所取的木棒和此边之前的长度sum之和小于指定边长 
		return false;
	return true;
}

void dfs(int k, int m, int side)
{
	int temp = 0;
	if(k == m + 1)				//base case
	{
		/*for(int i = 1;i <= m;i++)
			cout << x[i] << ", ";
		cout << endl;*/
		flag = true;
	}
	else
	{
		for(int i = 1;i <= m && !flag;i++)
		{
			if(jianzhi(i, side))
			{
				if(sum + a[i] == side)	//如果可以组成一个边 
				{
					temp = sum;		//回溯时用 
					sum = 0;		//sum准备计下一条边的边长 
				}
				else
					sum += a[i];
				tag[i] = 1;
				x[k] = a[i];
				dfs(k + 1, m, side); 
				x[k] = 0;
				tag[i] = 0;
				if(sum == 0)		//此处有点容易昏 
					sum = temp;
				else
					sum -= a[i];
			}
		}
	} 
}

void Clear(int m)
{
	for(int i = 1;i <= m;i++)
		tag[i] = 0;
}

int main()
{
	int T;
	cin >> T;
	int array_sum = 0;
	for(int i = 1;i <= T;i++)
	{
		int m;
		cin >> m;
		for(int j = 1;j <= m;j++)
		{
			cin >> a[j];
			array_sum += a[j];
		}
		if(array_sum % 4 != 0)				//第一个剪枝:如果总和不是4的倍数肯定不行 
			cout << "no" << endl;
		else
		{
			sort(a, a + m, greater<int>());
			dfs(1, m, array_sum / 4);		//看能否找到4个array_sum / 4的边 
			if(flag)
				cout << "yes" << endl;
			else
				cout << "no" << endl;
		}
		array_sum = 0;
		flag = false;
		Clear(m);
	}
	return 0;
} 

后来参考一下网上大牛的代码,发现自己写的dfs确实思路混乱了点,不如大牛的版本,而且它把dfs函数返回值设为bool的,参数也和状态的设置更加贴切,给我指了一条明路,下面是我参考之后改进的AC代码

#include<iostream>
#include<algorithm>
using namespace std;

int m;
int stick[22];
int tag[22];
int array_sum;

//count是目前完成了几个边,pos目前遍历到了第几个stick,res此棍子剩余所需长度 

bool dfs(int count, int pos, int res)    
{
    if(count == 3)        //如果已经拼好3根棍子,那么一定能拼好 
        return true;
    for(int i = pos;i <= m;i++)
    {
        if(tag[i] == 1)        //如果该stick没有被用过,那么我们才用 
            continue;    //continue:如果stick被用过,那么直接跳到下一层循环 
        tag[i] = 1;        //否则就是stick没用过的情况 
        if(stick[i] == res)
        {
            if(dfs(count + 1, 1, array_sum / 4))
                return true;
        }
        else if(stick[i] < res)
        { 
            if(dfs(count, i + 1, res - stick[i]))
                return true;
        }
        tag[i] = 0;
    }
    return false;
}

void Clear()
{
    for(int i = 1;i <= m;i++)
        tag[i] = 0;
}

int main()
{
    int T;
    cin >> T;
    for(int i = 1;i <= T;i++)
    {
        cin >> m;
        for(int i = 1;i <= m;i++)
        {
            cin >> stick[i];
            array_sum += stick[i];
        }
        if(array_sum % 4 != 0)
            cout << "no" << endl;
        else
        {
            if(dfs(0, 1, array_sum / 4))
                cout << "yes" << endl;
            else
                cout << "no" << endl;
        }
        array_sum = 0;
        Clear();   
    }
    return 0;
}

这道题总体来说值得一做,收获还是很多的。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值