暑假第一周

周一

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值