wikioi p2144 砝码称重 2
2013-08-27 13:24
274 查看
前奏,首先这题我一开始没有思绪,后来先用dfs+打表过了。
如果你不满足于代码,那你应该看看下面的内容。
这道题目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<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; }