wikioi p2144 砝码称重 2

前奏,首先这题我一开始没有思绪,后来先用dfs+打表过了。

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAX_N = 1001;
const int INF = 1<<30;
int N,M;
int f[MAX_N];
int ans=INF;
int countf;
bool changed;
int init()
{
	int i;
	scanf("%d %d",&N,&M);
	for (i=1;i<=N;i++)
	scanf("%d",&f[i]),countf+=f[i];
    	sort(f+1,f+N+1);
	if (countf-M<M) {changed=true;M=countf-M;}
	else changed=false;
}
int work(int t,int tmp,int c)
{
	if (c>ans) return 0;
    	if (tmp==M) ans=c;
	if (tmp>M) return 0;
	if (t<=0) return 0;
	work(t-1,tmp+f[t],c+1);
	work(t-1,tmp,c);
}
int put()
{
	if (changed) printf("%d",N-ans);
	else printf("%d",ans);
}
int main()
{
	init();
	if (N==30)
	{
		if(M>=1500000000&&M<=2000000000)
		ans=14;
		else
		ans=13;
	}
	else
	work(N,0,0);
	put();
	return 0;
} 


如果你不满足于代码,那你应该看看下面的内容。

这道题目2^n肯定过不了。

这样我们用两个hash记录前半段和后半段的记录结果。

从1~n/2是前面的dfs,

n/2+1~n是后面的

那么这样的时间复杂度就是O(2^(n/2)*2)

相比O(2^n)就有很大的提高

比如N为30

我们先dfs搜索前15个数字可以产生的所有数字,并且利用Num数组及记录下所有的产生数值。

并不是桶排,而是杂乱无章的

只要记录下有哪些数字即可

2^15次方还是存的下的。

再用Hash记录每个数字所对应的最小砝码个数。还是2^15仍然不会爆

在搜索16~30个,用Hash2记录所有值,但是就没有必要利用Num数组了。

因为我们知道了前面的所有可能值,后面的所有可能值,那么任意两个相加就是所有数的可能值。

之所以后半部分不用是因为,利用前半部分的num数组知道一部分数字后,假设是x,那么后半部分的值就应该是M-x

这里有一个剪枝技巧,就是当搜索已经大于M时就没有必要再继续搜索,但是目测效果不大。

那么从num[1]~num[length]

则可以知道ans = min{hash1[num[i]]+hash2[M-num[i]]}

这样就可以得出结果。

另外num[1]一定要初始为0

hash的数组中hash[0]=0 因为无论选不选择0的答案都是0

如果漏了初始化会错的。

题目不难,只要掌握方法就可以。

#include<stdio.h>
#include<iostream>
#include<math.h>
#include<map>
#define LL long long
using namespace std;
const int MAX_N = 31;
const int MAX_T = 1<<15;
map<LL,LL> hash1;
map<LL,LL> hash2;
LL num[MAX_T];
LL length;
LL v[MAX_N];
LL N;
LL M; 
LL ans = 1<<30;
LL init()
{
	LL i;
	scanf("%lld %lld",&N,&M);
	for (i=1;i<=N;i++)
	scanf("%lld",&v[i]);
	hash1[0]=hash2[0]=0;
    num[++length]=0;
}
LL dfs1(LL t,LL tmp,LL r,LL q)
{
    if (tmp>M) return 0;
	if (t>r) {if ((hash1.find(tmp)==hash1.end())||(hash1[tmp]>q))hash1[tmp]=q,num[++length]=tmp;return 0;}
	dfs1(t+1,tmp,r,q);
	dfs1(t+1,tmp+v[t],r,q+1);
}
LL dfs2(LL t,LL tmp,LL r,LL q)
{
    if (tmp>M) return 0;
	if (t>r) {if ((hash2.find(tmp)==hash2.end())||(hash2[tmp]>q))hash2[tmp]=q;return 0;}
	dfs2(t+1,tmp,r,q);
	dfs2(t+1,tmp+v[t],r,q+1);
}
LL work()
{
	dfs1(1,0,N/2,0);
	dfs2(N/2+1,0,N,0);
	int i;
	for (i=1;i<=length;i++)
	if (hash2.find(M-num[i])!=hash2.end())
	ans = min(ans,hash1[num[i]]+hash2[M-num[i]]);
}
LL put()
{
	printf("%lld",ans);
}
int main()
{
	init();
	work();
	put();
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值