for (int s = m; ; s = (s - 1) & m) {
// 使用子集合 s
if (s == 0) break;
}
for (int s = m; s < (1<<n); s = (s + 1) | m) {
// s 是 m 的超集合
}
原題:
https://cses.fi/problemset/task/2129
題意:
有 n 個人與 n 項任務,給定代價矩陣 cost[i][j],每人分到 exactly 一個任務,最小化總成本。
解題思路:
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false); cin.tie(nullptr);
int n; if(!(cin>>n)) return 0;
vector<vector<long long>> cost(n, vector<long long>(n));
for(int i=0;i<n;i++) for(int j=0;j<n;j++) cin>>cost[i][j];
const long long INF = (long long)4e18;
int M = 1<<n;
vector<long long> dp(M, INF);
dp[0] = 0;
for(int mask=0; mask<M; ++mask){
int i = __builtin_popcount((unsigned)mask); // 已分配的人數
if(i>=n) continue;
if(dp[mask] == INF) continue;
for(int j=0;j<n;j++){
if(!(mask & (1<<j))){
int nxt = mask | (1<<j);
dp[nxt] = min(dp[nxt], dp[mask] + cost[i][j]);
}
}
}
cout << dp[M-1] << "\n";
return 0;
}
原題:
https://cses.fi/problemset/task/1653
題意:
每個人重量 w[i],電梯容量 x,希望載完所有人所需最少趟數。
解題思路:
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false); cin.tie(nullptr);
int n; long long x;
if(!(cin>>n>>x)) return 0;
vector<long long> w(n);
for(int i=0;i<n;i++) cin>>w[i];
int M = 1<<n;
const long long INF = (long long)4e18;
vector<pair<int,long long>> dp(M, {n+1, INF});
dp[0] = {1, 0}; // 空集合視為開第一趟、重量 0(常見做法)
auto better = [](pair<int,long long> a, pair<int,long long> b){
if(a.first != b.first) return a.first < b.first;
return a.second < b.second;
};
for(int mask=0; mask<M; ++mask){
auto [r, cur] = dp[mask];
if(r==n+1) continue;
for(int i=0;i<n;i++){
if(!(mask & (1<<i))){
pair<int,long long> cand;
if(cur + w[i] <= x) cand = {r, cur + w[i]};
else cand = {r+1, w[i]};
int nxt = mask | (1<<i);
if(better(cand, dp[nxt])) dp[nxt] = cand;
}
}
}
cout << dp[M-1].first << "\n";
return 0;
}
原題:
https://leetcode.com/problems/partition-to-k-equal-sum-subsets/description/
題意:把陣列分成 k 個子集合,使每組和相等。
解題思路:
class Solution {
public:
bool canPartitionKSubsets(vector<int>& nums, int k) {
long long sum = accumulate(nums.begin(), nums.end(), 0LL);
if(sum % k) return false;
int n = nums.size();
int target = sum / k;
sort(nums.begin(), nums.end(), greater<int>());
if(nums[0] > target) return false;
int M = 1<<n;
vector<int> dp(M, -1);
dp[0] = 0;
for(int mask=0; mask<M; ++mask){
if(dp[mask] == -1) continue;
for(int i=0;i<n;i++){
if(!(mask & (1<<i))){
if(dp[mask] + nums[i] <= target){
int nxt = mask | (1<<i);
int val = (dp[mask] + nums[i]) % target;
if(dp[nxt] == -1) dp[nxt] = val;
// 可加速:若裝滿一桶(val==0)通常更有利,亦可不覆蓋舊值
}
}
}
}
return dp[M-1] == 0;
}
};
原題:
https://leetcode.com/problems/matchsticks-to-square/
題意:用一堆火柴是否能組成正方形(四邊等長)。
解題思路:
class Solution {
public:
bool makesquare(vector<int>& matchsticks) {
long long sum = accumulate(matchsticks.begin(), matchsticks.end(), 0LL);
if(sum % 4) return false;
int target = sum / 4;
int n = matchsticks.size();
sort(matchsticks.begin(), matchsticks.end(), greater<int>());
if(matchsticks[0] > target) return false;
int M = 1<<n;
vector<int> dp(M, -1);
dp[0] = 0;
for(int mask=0; mask<M; ++mask){
if(dp[mask] == -1) continue;
for(int i=0;i<n;i++){
if(!(mask & (1<<i))){
if(dp[mask] + matchsticks[i] <= target){
int nxt = mask | (1<<i);
int val = (dp[mask] + matchsticks[i]) % target;
if(dp[nxt] == -1) dp[nxt] = val;
}
}
}
}
return dp[M-1] == 0;
}
};