ccpc2016杭州D题HDU5937

本文分享了一道ACM竞赛中看似复杂的搜索题的解决过程,作者最初误以为是网络流问题,经过一系列思考和尝试,最终采用深度优先搜索(DFS)结合剪枝策略成功解决。文章详细记录了从建模失败到正确实现的全过程,包括剪枝策略的应用和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

upd:这篇是17年写的博客,结果后来18年出成月赛于是把这篇删了,现在还原又变成最新发布时间了。。。。

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5937

一开始认为是网络流,1边是1-9,另一边是1+1=2 1+2=3,但如何限制3个流量满流到右边才能对答案贡献为1?完全想不出,以为是个神建模。之前认为D是搜索,然后队友说不可能这个也是搜索,1次比赛出2个搜索的概率太小了,我就觉得嘛,与其挂机不如暴力。于是写了dfs,然后队友一测100 100 ..100,果然超时,队友尝试另一个题的时候,我想了想搜索东西,可行性剪枝,最优性剪枝,迭代加深,a*函数。。。。卧槽好像可以如果后面的全部选都达不到当前已记录的答案就return,最基本的最优性剪枝啊。。然后队友尝试完D打出GG,我立马加上这个剪枝,哈哈100 100 ..100能过辣,激动地交一发,队友一口奶,WA了,然后查错发现2+7=6了。。然后改掉,交一发,没奶,A了,哇这个搜索题A的人这么少嘛?很多人都不相信这是搜索吧。

 

#include<cstdio>
#include<cstring>
#define maxl 1001

int ans;
int a[10],cnt[21];
int t[21][3]=
{{0,0,0},{1,1,2},{1,2,3},{1,3,4},{1,4,5},{1,5,6},{1,6,7},{1,7,8},{1,8,9}
 		,{2,2,4},{2,3,5},{2,4,6},{2,5,7},{2,6,8},{2,7,9}
 		,{3,3,6},{3,4,7},{3,5,8},{3,6,9}
 		,{4,4,8},{4,5,9}
};

void prework()
{
	for(int i=1;i<=9;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=20;i++)
	if(t[i][0]==t[i][1])
		cnt[i]=cnt[i-1]+1;
	else
		cnt[i]=cnt[i-1]+2;
}

inline bool jug(int k,int i)
{
	if(t[k][0]==t[k][1] && i>1)
		return false;
	if(t[k][0]!=t[k][1] && i>2)
		return false;
	if(t[k][0]==t[k][1] && a[t[k][0]]<2*i)
		return false;
	if(a[t[k][0]]<i || a[t[k][1]]<i || a[t[k][2]]<i)
		return false;
	return true;
}

void dfs(int k,int sum)
{
	int i=0;
	if(cnt[20]-cnt[k-1]+sum<=ans)
		return;
	while(jug(k,i))
	{
		a[t[k][0]]-=i;a[t[k][1]]-=i;a[t[k][2]]-=i;sum+=i;
		if(k==20)
			ans=sum>ans? sum:ans;
		else
			dfs(k+1,sum);
		a[t[k][0]]+=i;a[t[k][1]]+=i;a[t[k][2]]+=i;sum-=i;
		i++;
	}
}

void mainwork()
{
	ans=0;
	dfs(1,0);
}

void print(int cas)
{
	printf("Case #%d: %d\n",cas,ans);
}

int main()
{
	int t;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		prework();
		mainwork();
		print(i);
	}
	return 0;
}

转载某神犇更加全面的剪枝,我还是太菜辣。。

  1. 对于遍历次数的优化
    仔细跟着思路模拟一下,会发现,对于 a+b=c 和 b+a=c 的情况,他们属于可以把 a+b=c 选择两次的情况
    而直接遍历 36 种可能显然把 a+b=c 只选一次的情况 遍历了两次
    因此,这里可以先标记下然后再进行判断,如果 a+b=c 不在 DFS 递归的栈里(已经遍历过只选一次的分支),那么不再遍历 b+a=c
  2. 剪枝
    • 当深度大于等于 36 的时候,显然已经找到答案了,可以直接返回
    • 如果把现在所有的数字除以 3 (每个式子有 3 个数字) 再加上已经遍历的深度都不可能比已有答案大,显然可以直接返回
    • 如果把当前已经遍历的深度(已选择的式子数目),和剩下能遍历的深度加起来都没有已有答案大,可以直接返回
  3. 预处理
    预处理到每个深度,其以后所有式子需要的每个数字的个数.当发现数字足够时,直接返回返回答案

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值