原題:
https://cses.fi/problemset/task/1093
題意:
將 {1..n} 分成兩組,兩組元素和相等,問共有幾種分法(順序與組內排列不計,取模 1e9+7)。
解題思路:
總和 S=n(n+1)/2,若為奇數則 0;否則計算「子集合和為 S/2」的種數。
注意:每個劃分會被「子集合 vs 補集」算到兩次 → 最終答案需 除以 2(用模逆元)。
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1'000'000'007;
int addmod(int a,int b){ a+=b; if(a>=MOD) a-=MOD; return a; }
int main(){
ios::sync_with_stdio(false); cin.tie(nullptr);
int n; if(!(cin>>n)) return 0;
long long S = 1LL*n*(n+1)/2;
if(S & 1LL){ cout<<0<<"\n"; return 0; }
int T = (int)(S/2);
vector<int> dp(T+1, 0);
dp[0] = 1;
for(int x=1; x<=n; ++x){
for(int s=T; s>=x; --s){
dp[s] = addmod(dp[s], dp[s-x]);
}
}
// 除以 2(模逆元 2^{-1} = (MOD+1)/2)
long long inv2 = (MOD + 1LL)/2;
cout << (long long)dp[T] * inv2 % MOD << "\n";
return 0;
}
原題:
https://cses.fi/problemset/task/1746
題意:
長度 n 的陣列,值域 1..m。給定部分位置(0 代表可自由指定),要求相鄰元素差的絕對值 ≤ 1,問有幾種填法(取模)。
解題思路:
狀態:dp[x] = 目前位置 i,且 a[i]=x 的方法數
轉移:newdp[x] = dp[x-1] + dp[x] + dp[x+1](留意邊界 1、m)
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1'000'000'007;
int main(){
ios::sync_with_stdio(false); cin.tie(nullptr);
int n, m; if(!(cin>>n>>m)) return 0;
vector<int> a(n);
for(int i=0;i<n;i++) cin>>a[i];
vector<int> dp(m+1, 0), ndp(m+1, 0);
if(a[0]==0){
for(int x=1;x<=m;x++) dp[x]=1;
}else{
dp[a[0]] = 1;
}
for(int i=1;i<n;i++){
fill(ndp.begin(), ndp.end(), 0);
if(a[i]==0){
for(int x=1;x<=m;x++){
long long v = dp[x];
if(x>1) v += dp[x-1];
if(x<m) v += dp[x+1];
ndp[x] = (int)(v % MOD);
}
}else{
int x = a[i];
long long v = dp[x];
if(x>1) v += dp[x-1];
if(x<m) v += dp[x+1];
ndp[x] = (int)(v % MOD);
}
dp.swap(ndp);
}
long long ans = 0;
for(int x=1;x<=m;x++) ans = (ans + dp[x]) % MOD;
cout << ans << "\n";
return 0;
}
原題:
https://leetcode.com/problems/house-robber-ii/description/
重點:首尾相鄰 → 不能同時取 0 與 n-1。
做法:答案 = max(rob(linear, 0..n-2), rob(linear, 1..n-1));線性版即經典 198 題:dp[i]=max(dp[i-1], dp[i-2]+val)。
class Solution {
int robLinear(const vector<int>& a, int l, int r){
int pre2 = 0, pre1 = 0; // dp[i-2], dp[i-1]
for(int i=l;i<=r;i++){
int cur = max(pre1, pre2 + a[i]);
pre2 = pre1; pre1 = cur;
}
return pre1;
}
public:
int rob(vector<int>& nums) {
int n = nums.size();
if(n==1) return nums[0];
return max(robLinear(nums, 0, n-2), robLinear(nums, 1, n-1));
}
};
原題:
https://leetcode.com/problems/word-break/
題意:
判斷字串能否被詞典完整切分。
狀態:
dp[i]=s[0..i) 可切分;轉移:存在 j<i 且 dp[j]==true 且 s[j..i) 在字典中 → dp[i]=true。
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> st(wordDict.begin(), wordDict.end());
int n = s.size(), maxL = 0;
for (auto &w: wordDict) maxL = max(maxL, (int)w.size());
vector<char> dp(n+1, 0);
dp[0] = 1;
for(int i=1;i<=n;i++){
for(int len=1; len<=maxL && len<=i; ++len){
if(!dp[i-len]) continue;
if(st.count(s.substr(i-len, len))){ dp[i]=1; break; }
}
}
return dp[n];
}
};