2017-2018 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) B - Enlarging Enthusiasm dp好题
感觉做到过好多的dp题都会和单调性结合在一起。
思路:dp[ s ][ pre ][ res ] 表示的是已选择了s,上一个是pre, 还有res 的分数的方案数。
然后再枚举下一个位置的时候,把其他位置的也减去这个值,因为是单调递增的所以不会多减,
这样就能保证pre 和 当前要枚举的 i 位置的差值永远为 a[ pre ] - a[ i ]
#include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define PLL pair<LL, LL> #define PLI pair<LL, int> #define PII pair<int, int> #define SZ(x) ((int)x.size()) #define ull unsigned long long using namespace std; const int N = 1e5 + 7; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const int mod = 1e9 + 7; const double eps = 1e-8; const double PI = acos(-1); int n, m, ans, mx, a[12], dp[1<<12][12][701], num[1<<12]; int dfs(int s, int pre, int res) { if(res < 0) return 0; if(s+1 == 1<<n) return 1; if(~dp[s][pre][res]) return dp[s][pre][res]; dp[s][pre][res] = 0; for(int i = 0; i < n; i++) if(!(s>>i&1)) dp[s][pre][res] += dfs(s|1<<i, i, res-max(a[pre]-a[i]+1, 0)*(n-num[s])); return dp[s][pre][res]; } int main() { memset(dp, -1, sizeof(dp)); scanf("%d%d", &n, &m); for(int i = 0; i < n; i++) { scanf("%d", &a[i]); mx = max(mx, a[i]); } for(int i = 1; i < (1<<n); i++) num[i] = num[i-(i&-i)] + 1; for(int i = 0; i < n; i++) if(a[i] != mx) ans += dfs(1<<i, i, m-(mx-a[i]+1)*n); printf("%d\n", ans); return 0; } /* */