洛谷Oj-P1441 砝码称重-深搜+DP/Bitset
2018-03-17 09:59
330 查看
题目描述:
现有n个砝码,重量分别为a1,a2,a3,……,an,在去掉m个砝码后,问最多能称量出多少不同的重量(不包括0)。
代码①:
代码②:
代码③:
代码④:
代码⑤:
代码⑥:
解决方法:
以下是代码⑥的搜索树。
以数据
5 2
1 2 3 4 5
为例
现有n个砝码,重量分别为a1,a2,a3,……,an,在去掉m个砝码后,问最多能称量出多少不同的重量(不包括0)。
代码①:
int n,m; int w[30]; bool book[30]; set<int> s;//想到用集合来去重 int ans = -inf; void dfs(int cnt) { if(cnt == m)//如果被丢掉的砝码数到了m { int sum; for(int i = 1; i <= (1 << n) - 1; ++i)//子集枚举 { sum = 0; for(int j = 0; j <= n - 1; ++j) { if(i & (1 << j) && book[j] == false)//如果i的第j位上为1且第j个砝码没被丢掉 sum += w[j]; } s.insert(sum);//加入集合 } ans = max(ans,(int)s.size()); s.clear();//重置集合 } for(int i = 0; i <= n - 1; ++i)//效率极低,因为求的是全排列,有许多重复的状态 { if(book[i] == false) { book[i] = true; dfs(cnt + 1); 4000 book[i] = false; } } return; } int main() { cin >> n >> m; for(int i = 0; i <= n - 1; ++i)//注意下标 cin >> w[i]; dfs(0); if(m == 0) cout << ans << endl; else cout << ans - 1 << endl;//子集枚举时会出现要用的砝码和被去掉的砝码相同的情况,导致重量0可被称出 return 0; }
代码②:
int n,m; int w[30]; bool book[30]; set<int> s; int ans = -inf; int main()//双重子集枚举,超时 { cin >> n >> m; for(int i = 0; i <= n - 1; ++i) cin >> w[i]; //枚举去掉的砝码的子集 for(int i = 0; i <= (1 << n) - 1; ++i) { int cnt = 0; memset(book,0,sizeof(book)); for(int j = 0; j <= n - 1; ++j) { if(i & (1 << j)) { cnt++; book[j] = true; } } if(cnt == m)//如果i的二进制中出现了m个1 { int sum = 0; //枚举砝码的子集 //若j从0开始,会把sum=0包括进去 for(int j = 1; j <= (1 << n) - 1; ++j) { sum = 0; for(int k = 0; k <= n - 1; ++k) //j的第k位为1,i的第k位为0 if((j & (1 << k)) && book[k] == false) sum += w[k]; s.insert(sum); ans = max(ans,(int)s.size()); } s.clear(); } } if(m == 0) cout << ans << endl; else cout << ans - 1 << endl; return 0; }
代码③:
int n,m; int w[30]; bool book[30]; bool mark[5000][30];//判断重复状态的数组 int sz = 1; set<int> s; int ans = -inf; void copy() { for(int i = 1; i <= n; ++i) if(book[i] == true) mark[sz][i] = true; sz++; return; } bool check_1(bool a[],bool b[]) { for(int i = 1; i <= n; ++i) if(a[i] != b[i]) return false; return true; } bool check_2() { for(int i = 1; i <= sz - 1; ++i) for(int j = 1; j <= n; ++j) if(check_1(book,mark[i]) == true) return true; return false; } void dfs(int cnt) { if(cnt == m) { if(check_2() == true)//加入了重复状态判断以剪枝,依然超时。因为全排列的搜索重复状态太多了 return; copy(); int sum; for(int i = 1; i <= (1 << n) - 1; ++i) { sum = 0; for(int j = 0; j <= n - 1; ++j) { if(i & (1 << j) && book[j] == false) sum += w[j]; } s.insert(sum); } ans = max(ans,(int)s.size()); s.clear(); } for(int i = 0; i <= n - 1; ++i) { if(book[i] == false) { book[i] = true; dfs(cnt + 1); book[i] = false; } } return; } int main() { cin >> n >> m; for(int i = 0; i <= n - 1; ++i) cin >> w[i]; dfs(0); if(m == 0) cout << ans << endl; else cout << ans - 1 << endl; return 0; }
代码④:
int n,m; int w[30]; bool book[30]; bool dp[2010]; int ans = -inf; void solve()//01背包+滚动数组 { memset(dp,0,sizeof(dp)); dp[0] = true;//初始化 for(int i = 1; i <= n; ++i)//对于每一个砝码 { if(book[i] == true)//如果被丢掉了 continue;//跳过 for(int j = 2000; j >= 0; --j)//最大重量20*100 = 2000。倒序判断 if(dp[j] == true && j + w[i] <= 2000)//如果重量j能被称出且在重量j的基础上加上w[i]不超重 dp[j + w[i]] = true;//则重量j + w[i]也能被称出 } int tot = 0; for(int i = 1; i <= 2000; ++i) if(dp[i] == true) tot++; ans = max(ans,tot);//更新答案 return; } void dfs(int id,int cnt)//对当前的砝码,有丢与不丢两种选择,2^n种结果,这正是子集的大小(全排列的大小为n!)。这是枚举子集的搜索!!! { if(cnt > m)//针对cnt剪枝,丢得太多啦 return; if(id == n + 1)//针对id的边界条件 { if(cnt == m) solve(); return; } dfs(id + 1,cnt);//不去掉砝码id,对砝码id + 1进行操作 book[id] = true;//去掉砝码id dfs(id + 1,cnt + 1);//去掉砝码id,对砝码id + 1进行操作 book[id] = false;//我从来没有动过砝码id,别瞎说啊 return; } int main() { cin >> n >> m; for(int i = 1; i <= n; ++i) cin >> w[i]; dfs(1,0); cout << ans << endl; return 0; }
代码⑤:
int n,m; int w[30]; int ans = -inf; int count_one(int x)//数出x的二进制中1的个数 { int cnt = 0; for(int i = 0; i <= n - 1; ++i) if(x & (1 << i)) cnt++; return cnt; } int main() { cin >> n >> m; for(int i = 0; i <= n - 1; ++i) cin >> w[i]; for(int i = 0; i <= (1 << n) - 1; ++i) { if(count_one(i) == n - m)//这儿用的是n-m,更好 { bitset<2010> b; b[0] = 1; for(int j = 0; j <= n - 1; ++j) if(i & (1 << j)) b = b | b << w[j]; ans = max(ans,(int)b.count()); } } cout << ans - 1 << endl;//不要忘了排除重量0 return 0; }
代码⑥:
int n,m; int w[30]; bool book[30]; bool dp[2010]; int prev;// ! int ans = -inf; void solve() { memset(dp,0,sizeof(dp)); dp[0] = true; for(int i = 1; i <= n; ++i) { if(book[i] == true) continue; for(int j = 2000; j >= 0; --j) if(dp[j] == true && j + w[i] <= 2000) dp[j + w[i]] = true; } int tot = 0; for(int i = 1; i <= 2000; ++i) if(dp[i] == true) tot++; ans = max(ans,tot); return; } void dfs(int cnt)//子集枚举搜索的另一种写法。不断地丢砝码 { if(cnt == m) { solve(); return; } for(int i = prev + 1; i <= n; ++i) { book[i] = true;//丢掉 prev = i;//上一个被丢的砝码的编号 dfs(cnt + 1); book[i] = false;//捡回来 } return; } int main() { cin >> n >> m; for(int i = 1; i <= n; ++i) cin >> w[i]; dfs(0); cout << ans << endl; return 0; }
解决方法:
以下是代码⑥的搜索树。
以数据
5 2
1 2 3 4 5
为例
相关文章推荐
- 洛谷Oj-P2347 砝码称重-Bitset/多重背包
- 砝码称重(dp)
- ssl1072-砝码称重【dp练习】
- 洛谷 1441 砝码称重 搜索+DP 解题报告
- 洛谷 P1441 砝码称重--搜索位运算优化
- 砝码称重问题【dp】
- LuoguP1441 砝码称重 解题报告【搜索+背包型DP】
- 【dp】砝码称重
- P1441 砝码称重
- 砝码称重_DP
- P1441 砝码称重
- 洛谷P1441 砝码称重
- 洛谷P1441 砝码称重 bitset状压
- 砝码称重
- 51 nod 砝码称重(贪心+进制转换思想)
- P2347 砝码称重
- [HDOJ5890]Eighty seven(暴力,dp,bitset)
- hdu_5890_Eighty seven(bitset优化DP)
- 砝码称重问题
- BestCoder Round #70(B)dp(STL bitset的妙用)