周一
D. Infinite Set(二进制+dp)
关键是看成二进制,乘以4相当于补两个0,乘2加1相当于补一个1
dpi表示二进制表示长度为i的方案数,那么有dpi = dpi-1+dpi-2
那么考虑怎么处理已经有的数
首先要去掉一些重复的数,也就是一个数如果能被另一个数生成,就去掉
这个很好处理,就暴力枚举当前数可能由哪些数生成,然后用map看是否是已经有的数即可
那么用gi表示原本就已经有的数长度为i的有多少个
那么dpi = dpi + dpi-1 + gi
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
int a[N], dp[N], g[N], n, p;
unordered_map<int, int> mp;
int len(int x)
{
int res = 0;
while(x) res++, x >>= 1;
return res;
}
bool check(int x)
{
while(x)
{
if(x % 4 == 0)
{
x /= 4;
if(mp[x]) return false;
}
else if((x - 1) % 2 == 0)
{
x = (x - 1) / 2;
if(mp[x]) return false;
}
else break;
}
return true;
}
int main()
{
scanf("%d%d", &n, &p);
_for(i, 1, n) scanf("%d", &a[i]), mp[a[i]] = 1;
_for(i, 1, n)
if(check(a[i]))
g[len(a[i])]++;
dp[1] = g[1];
_for(i, 2, p)
dp[i] = (dp[i - 1] + dp[i - 2] + g[i]) % mod;
int ans = 0;
_for(i, 1, p) ans = (ans + dp[i]) % mod;
printf("%d\n", ans);
return 0;
}
周六
P3052 [USACO12MAR]Cows in a Skyscraper G(dfs+剪枝)
这题的数据范围只有18很关键,很小的数据范围一般只有以下两个可能
①dfs 二进制 ②状压dp
事实上这题两种思路均可,先看dfs
因为答案最大就18,所以可以直接枚举答案
然后爆搜,每次看当前的奶牛放到哪一个组里面
这里要加一个剪枝,也就是第i个奶牛一定是放在1~i个组中
因为极限情况下一个奶牛一个组,那么当前奶牛也就是放到第i组,放到i组以后,那就代表前面有组是空的,那肯定可以去掉。
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 20;
int w[N], sum[N], n, m, num;
bool dfs(int x)
{
if(x == n + 1) return true;
_for(i, 1, min(x, num))
{
if(sum[i] + w[x] <= m)
{
sum[i] += w[x];
if(dfs(x + 1)) return true;
sum[i] -= w[x];
}
}
return false;
}
int main()
{
scanf("%d%d", &n, &m);
_for(i, 1, n) scanf("%d", &w[i]);
_for(i, 1, n)
{
num = i;
memset(sum, 0, sizeof sum);
if(dfs(1))
{
printf("%d\n", i);
break;
}
}
return 0;
}
P3052 [USACO12MAR]Cows in a Skyscraper G(状压dp)
这题在于用一个辅助数组记录一些信息,转移的时候需要
f[S]表示状态为S的最小组数
g[S]表示状态为S,组数为f[S]时,最大剩余的容量
对于当前状态,枚举其子状态,转移根据g[pre]的值可以判断是否要加多一个组
关键在于g数组的更新
对于当前状态来说,剩余的最大容量肯定是某一个奶牛放进去的剩余容量。那么我们只需要在所有子状态的g值减去放入奶牛的值中取最大,就一定可以包括最大容量的情况。
最后,写完之后肉眼静态查错,不要打错变量名!!
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 20;
int f[1 << N], g[1 << N], w[N], n, m;
int main()
{
scanf("%d%d", &n, &m);
_for(i, 1, n) scanf("%d", &w[i]);
memset(f, 0x3f, sizeof f);
f[0] = 1;
g[0] = m;
rep(S, 0, 1 << n)
{
_for(j, 1, n)
if(S & (1 << (j - 1)))
{
int pre = S ^ (1 << (j - 1));
if(g[pre] >= w[j] && f[pre] <= f[S])
{
if(f[pre] < f[S])
{
f[S] = f[pre];
g[S] = g[pre] - w[j];
}
else g[S] = max(g[S], g[pre] - w[j]);
}
else if(g[pre] < w[j] && f[pre] + 1 <= f[S])
{
if(f[pre] + 1 < f[S])
{
f[S] = f[pre] + 1;
g[S] = m - w[j];
}
else g[S] = max(g[S], m - w[j]);
}
}
}
printf("%d\n", f[(1 << n) - 1]);
return 0;
}
周日
E. Fish(状压dp+概率)
n只有18,状压dp
状态转移的时候根据概率计算即可,注意区分加法原理和乘法原理
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 20;
double a[N][N], dp[1 << N];
int n;
int main()
{
scanf("%d", &n);
_for(i, 1, n)
_for(j, 1, n)
scanf("%lf", &a[i][j]);
dp[0] = 1;
rep(S, 0, 1 << n)
_for(i, 1, n)
if(S & (1 << (i - 1)))
{
int pre = S ^ (1 << (i - 1)), cnt = 0;
double s = 0;
_for(j, 1, n)
if(!(pre & (1 << (j - 1))))
{
cnt++;
s += a[j][i];
}
dp[S] += dp[pre] * s / (cnt * (cnt - 1) / 2);
}
int t = (1 << n) - 1;
_for(i, 1, n) printf("%.6f ", dp[t ^ (1 << (i - 1))]);
return 0;
}