1.普通状态压缩 DP
I.P1896 [SCOI2005] 互不侵犯
\(f_{i,j,st}\) 表示前 \(i\) 行中放置了 \(j\) 个国王,当前行状态为 \(st\) 的方案数。可以预处理出合法的状态与其 popcount
,转移时枚举当前行状态和上一行状态,合法就转移。
const int N = 20,inf = 1e9,mod = 998244353;
const ll inff = 1e18;
int n,k,f[N][110][1000];
vector<int> v; int pcnt[1000];il void solve() {//------------code------------read(n,k);rep(st,0,(1 << n) - 1) if (!((st << 1) & st) && !((st >> 1) & st)) v.pb(st),pcnt[st] = __builtin_popcount(st);f[0][0][0] = 1;rep(i,1,n) for (auto st : v) for (auto stt : v)if (!(stt & st) && !((stt << 1) & st) && !((stt >> 1) & st)) {rep1(j,k,pcnt[st]) f[i][j][st] += f[i - 1][j - pcnt[st]][stt];}int ans = 0;for (auto st : v) ans += f[n][k][st];write(ans,'\n');// cerr << "Time : " << (db)(end - start) / CLOCKS_PER_SEC << " s" << endl;return ;
}
II.P2704 [NOI2001] 炮兵阵地
考虑将一行某个位置能否部署状压成一个二进制数,然后判断状态合法时可以直接把它们按位与起来。\(f_{i,st,st'}\) 表示前 \(i\) 行中,当前行状态为 \(st\),上一行状态为 \(st'\),最多能摆放的炮兵部队的数量。压掉一维就能过。
const int N = 110,inf = 1e9,mod = 998244353;
const ull base = 131,base_ = 233;
const ll inff = 1e18;
int n,m,a[N];
int f[3][1034][1034];
vector<PII> v;il void solve() {//------------code------------read(n,m);rep(i,1,n) {string s; cin >> s;rep(j,0,m - 1) a[i] += (s[j] == 'H') * (1 << (m - 1 - j));}rep(st,0,(1 << m) - 1) if (!(st & (st >> 1)) && !(st & (st >> 2))) v.pb(st,__builtin_popcount(st));memset(f,-0x3f,sizeof f);for (auto x : v) f[1][x.fst][0] = x.snd;rep(i,2,n)for (auto st : v) if (!(st.fst & a[i]))for (auto stt : v) if (!(st.fst & stt.fst) && !(stt.fst & a[i - 1]))for (auto sttt : v) if (!(st.fst & sttt.fst) && !(stt.fst & sttt.fst) && !(sttt.fst & a[i - 2]))chmax(f[i % 3][st.fst][stt.fst],f[(i - 1) % 3][stt.fst][sttt.fst] + st.snd);int ret = 0;for (auto st : v) if (!(st.fst & a[n]))for (auto stt : v) if (!(st.fst & stt.fst) && !(stt.fst & a[n - 1]))chmax(ret,f[n % 3][st.fst][stt.fst]);write(ret,'\n');return ;
}
III.P1879 [USACO06NOV] Corn Fields G
板子。\(f_{i,st}\) 表示前 \(i\) 行中当前行状态为 \(st\) 的种植方案数。
const int N = 22,inf = 1e9,mod = 1e8;
const ull base = 131,base_ = 233;
const ll inff = 1e18;
int n,m,a[N]; ll f[N][4100];
vector<int> v;il void solve() {//------------code------------read(n,m);rep(i,1,n)rep(j,1,m) {int x; read(x);a[i] += (1 << (m - j)) * (!x);}rep(st,0,(1 << m) - 1) if (!(st & (st << 1))) v.pb(st);f[0][0] = 1;rep(i,1,n)for (auto st : v) if (!(st & a[i]))for (auto stt : v) if (!(st & stt) && !(stt & a[i - 1]))f[i][st] = (f[i][st] + f[i - 1][stt]) % mod;ll ans = 0;for (auto st : v) ans = (ans + f[n][st]) % mod;write(ans,'\n');return ;
}
IV.P3052 [USACO12MAR] Cows in a Skyscraper G
\(f_{st}\) 表示当前已将哪些物品进行分组,最小的分组数量。维护一个当前 \(f_{st}\) 的最大剩余体积,每次能放下就放,放不下就增加组数,同时更新最大剩余体积。
const int N = 3e5 + 10,inf = 1e9,mod = 998244353;
const ull base = 131,base_ = 233;
const ll inff = 1e18;
int n,m,w[20]; PII f[N];il void solve() {//------------code------------read(n,m);rep(i,1,n) read(w[i]);rep(st,1,(1 << n) - 1) f[st] = {-inf,inf};f[0] = {m,1};rep(st,1,(1 << n) - 1)rep(i,0,n - 1) if (st >> i & 1) {if (f[st].snd >= f[st ^ (1 << i)].snd && f[st ^ (1 << i)].fst >= w[i + 1]) {chmax(f[st].fst,f[st ^ (1 << i)].fst - w[i + 1]);f[st].snd = f[st ^ (1 << i)].snd;} else if (f[st].snd >= f[st ^ (1 << i)].snd + 1 && f[st ^ (1 << i)].fst < w[i + 1]) {chmax(f[st].fst,m - w[i + 1]);f[st].snd = f[st ^ (1 << i)].snd + 1;}// cerr << st << " " << i << " " << f[st].snd << '\n';}write(f[(1 << n) - 1].snd,'\n');return ;
}
V.P2396 yyy loves Maths VII
显然有 \(\mathcal{O(2^n n)}\) 的做法,但是 1 s
可能跑不过,考虑优化 \(n\) 的那一维,每次取出状态的 lowbit
来进行转移,并减掉它,这样可以减少大量无用的判断,能过此题。最慢的点跑了有 600 ms
。
const int N = 2e7 + 10,inf = 1e9,mod = 1e9 + 7;
const ull base = 131,base_ = 233;
const ll inff = 1e18;
int n,m,a[N],b[3],f[N],g[N];il int lowbit(int x) { return x & -x; }il void solve() {//------------code------------read(n); rep(i,1,n) read(a[(1 << i - 1)]);read(m); rep(i,1,m) read(b[i]);f[0] = 1;rep(st,1,(1 << n) - 1) {g[st] = g[st ^ lowbit(st)] + a[lowbit(st)];bool fl = 1;rep(i,1,m) if (g[st] == b[i]) { fl = 0; break; }if (!fl) continue;int x = st;for (; x; x -= x & -x) f[st] = (f[st] + f[st ^ lowbit(x)]) % mod;}write(f[(1 << n) - 1],'\n');return ;
}
VI.P2831 [NOIP2016 提高组] 愤怒的小鸟
可以发现两个点可以唯一确定一条抛物线,预处理 \(sta_{i,j}\) 表示第 \(i\) 个点和第 \(j\) 个点组成的抛物线经过的点,对其做状压处理。现在就有一个 \(\mathcal{O(T2^n n^2)}\) 的做法,对于当前状态枚举接下来打哪条抛物线。这样并不能通过所有测试点,考虑优化。发现最终需要的答案是 \(f_{2^n - 1}\),其余有 \(f\) 对最终答案并没有贡献,那么它是无用的,显然我们最终状态 \(2^n - 1\) 在二进制下是有 \(n\) 个 1
的,那么每次对于当前状态要推到最终状态,二进制下第一个为 0
的位置到最后一定会被转移掉,变为 1
,那么我们就可以找到这个位置,再枚举它和其它的点的抛物线,用此抛物线的 \(sta\) 来转移就足矣。最后时间复杂度是 \(\mathcal{O(T2^n n)}\) 的。
const int N = 270010,inf = 1e9,mod = 998244353;
const ull base = 131,base_ = 233;
const db eps = 1e-8;
const ll inff = 1e18;
int n,m; pair<db,db> a[N];
int cnt[20][20],f[N];il void solve() {//------------code------------read(n,m);rep(i,1,n) cin >> a[i].fst >> a[i].snd;rep(i,1,n) rep(j,i + 1,n) {db x = a[i].fst,y = a[i].snd;db xx = a[j].fst,yy = a[j].snd;if (fabs(x - xx) < eps) { cnt[i][j] = cnt[j][i] = 0; continue; }db A = (y / x - yy / xx) / (x - xx),B = y / x - x * A;// cerr << i << " " << j << " " << A << " " << B << '\n';if (A >= 0) { cnt[i][j] = cnt[j][i] = 0; continue; }cnt[i][j] = 0;rep(k,1,n) if (fabs(A * a[k].fst * a[k].fst + B * a[k].fst - a[k].snd) < eps)cnt[i][j] += (1 << k - 1);cnt[j][i] = cnt[i][j];}rep(st,0,(1 << n) - 1) f[st] = inf;f[0] = 0;rep(st,0,(1 << n) - 2) {int p = -1;rep(i,0,n - 1) if (!(st >> i & 1)) { p = i; break; }chmin(f[st | (1 << p)],f[st] + 1);rep(i,1,n) if (i != p && ~cnt[p + 1][i]) chmin(f[st | cnt[p + 1][i]],f[st] + 1);}write(f[(1 << n) - 1],'\n');return ;
}
VII.P2473 [SCOI2008] 奖励关
显然有 \(f_{i,st}\) 表示前 \(i\) 次中,吃过的宝物的集合为 \(st\) 的最大期望分值。然后你会发现对于 \(f_i\),有一些状态是明显不合法的,此题起点唯一,但终点不唯一且有一些状态不可达的情况,所以需要倒推。
const int N = 20,inf = 1e9,mod = 998244353;
const ull base = 131,base_ = 233;
const ll inff = 1e18;
int k,n,s[N],p[N];
db f[110][32770];il void solve() {//------------code------------read(k,n);rep(i,1,n) {read(p[i]); s[i] = 0;int x; read(x);while (x) {s[i] |= (1 << x - 1);read(x);}}f[k + 1][(1 << n) - 1] = 0;rep1(i,k,1)rep(st,0,(1 << n) - 1) {// cerr << i << " " << st << '\n';rep(j,1,n) if ((st & s[j]) == s[j]) f[i][st] += max((f[i + 1][st | (1 << j - 1)] + p[j] * 1.0),f[i + 1][st]) / (db)n;else f[i][st] += f[i + 1][st] / (db)n;}db ans = f[1][0];printf("%.6lf\n",ans);return ;
}
等待更新。