大三第一周训练

周三

前段时间没有写日报,而是写专题了

概率/期望dp专题_Alex Su (*^▽^*)的博客-CSDN博客

博弈论专题_Alex Su (*^▽^*)的博客-CSDN博客

现在觉得写日报才每天有收获感,有动力,于是又开始写日报

P3802 小魔女帕琪(手推概率)

这题就是手算,算出1~7触发的概率,再算一下2~8触发的概率,发现是一样的,然后就可以推出答案了

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

int main()
{
    double a[10], N;
    _for(i, 1, 7) scanf("%lf", &a[i]), N += a[i];

    double ans = (N - 6) * 5040;
    _for(i, 1, 7) ans *= a[i] / max(N - i + 1, 1.0); 
    printf("%.3f\n", ans);
   
	return 0;
}

P1850 [NOIP2016 提高组] 换教室(期望dp+floyed)

这题的期望比较特殊,它是比较独立的,概率不需要连乘

也就是说期望体现在两个相邻时间段之间的贡献。这个时候就不是那种比较经典的期望dp了。

细节上,注意初始化和边界问题。

floyed的时候注意自己自己到自己为0,我忽略了这个卡了很久。

#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 = 2e3 + 10;
const int M = 300 + 10;
int n, m, v, e, c[N], d[N], g[M][M];
double k[N], dp[N][N][2];

int main()
{
    scanf("%d%d%d%d", &n, &m, &v, &e);
    _for(i, 1, n) scanf("%d", &c[i]);
    _for(i, 1, n) scanf("%d", &d[i]);
    _for(i, 1, n) scanf("%lf", &k[i]);
    memset(g, 0x3f, sizeof g);
    while(e--)
    {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        g[a][b] = g[b][a] = min(g[a][b], w);
    }

    _for(K, 1, v)
        _for(i, 1, v)
            _for(j, 1, v)
                g[i][j] = min(g[i][j], g[i][K] + g[K][j]);
    _for(i, 1, v) g[i][i] = 0;    //写弗洛伊德时记得要加上自己和自己为0
    
    _for(i, 0, n)
        _for(j, 0, m) 
            dp[i][j][0] = dp[i][j][1] = 1e9;
    dp[1][1][1] = dp[1][0][0] = 0;
    _for(i, 2, n)
        _for(j, 0, m)
        {
            int c1 = c[i - 1], c2 = c[i], d1 = d[i - 1], d2 = d[i];
            dp[i][j][0] = min(dp[i - 1][j][0] + g[c1][c2], dp[i - 1][j][1]
                        + k[i - 1] * g[d1][c2] + (1 - k[i - 1]) * g[c1][c2]);
            if(j > 0)
                dp[i][j][1] = min(dp[i - 1][j - 1][0] + k[i] * g[c1][d2] + (1 - k[i]) * g[c1][c2],
                         dp[i - 1][j - 1][1] + k[i - 1] * k[i] * g[d1][d2] + (1 - k[i - 1]) * k[i] * g[c1][d2] + 
                        k[i - 1] * (1 - k[i]) * g[d1][c2] + (1 - k[i - 1]) * (1 - k[i]) * g[c1][c2]);
        }
    
    double ans = 1e9;
    _for(i, 0, m) ans = min(ans, min(dp[n][i][0], dp[n][i][1]));
    printf("%.2f\n", ans);
   
	return 0;
}

E. Vasya and Magic Matrix(dp+前缀和优化)

令N=nm,从数据范围来看,是一个O(N)的算法

直接dp显然要O(N^2)

把式子拆开后发现有很多前缀和,所以可以预处理前缀和来优化

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

typedef long long ll;
const int N = 1e6 + 10;
const int mod = 998244353;
ll dp[N], xx[N], x[N], y[N], yy[N], f[N];
struct node 
{ 
    int val, x, y; 
}ve[N];
int n, m, r, c, cnt;

ll binpow(ll a, ll b)
{
    ll res = 1;
    for(; b; b >>= 1)
    {
        if(b & 1) res = res * a % mod;
        a = a * a % mod;
    }
    return res;
}

ll inv(ll x) { return binpow(x, mod - 2); }

bool cmp(node a, node b)
{
    return a.val < b.val;
}

int main()
{
    scanf("%d%d", &n, &m);
    _for(i, 1, n)
        _for(j, 1, m)
        {
            int x; scanf("%d", &x);
            ve[++cnt] = {x, i, j};
        }
    scanf("%d%d", &r, &c);

    sort(ve + 1, ve + cnt + 1, cmp);
    _for(i, 1, cnt)
    {
        x[i] = (x[i - 1] + ve[i].x) % mod; xx[i] = (xx[i - 1] + ve[i].x * ve[i].x) % mod;
        y[i] = (y[i - 1] + ve[i].y) % mod; yy[i] = (yy[i - 1] + ve[i].y * ve[i].y) % mod;
    }

    ll tot = 0;
    _for(i, 1, cnt)
    {
        if(i == 1 || ve[i].val != ve[i - 1].val) tot = i - 1;
        dp[i] = (tot * ve[i].x * ve[i].x % mod + xx[tot] - 2 * ve[i].x * x[tot] % mod + 
                tot * ve[i].y * ve[i].y % mod + yy[tot] - 2 * ve[i].y * y[tot] % mod + f[tot]) % mod;
        dp[i] = dp[i] * inv(tot) % mod;
        f[i] = (f[i - 1] + dp[i]) % mod;
        if(ve[i].x == r && ve[i].y == c)
        {
            printf("%lld\n", dp[i]);
            break;
        }
    }

	return 0;
}

P1365 WJMZBMR打osu! / Easy(期望dp)

这题比较考数学

令Fi表示以i为结尾分数的期望。发现要用到以i-1为结尾的长度的期望,注意是它的期望。

因此还要维护Li。

#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 = 3e5 + 10;
long double F[N], L[N];
char s[N];
int n;

int main()
{
    scanf("%d%s", &n, s + 1);
    _for(i, 1, n)
    {
        if(s[i] == 'o')
        {
            F[i] = F[i - 1] + 2 * L[i - 1] + 1;
            L[i] = L[i - 1] + 1;
        }
        else if(s[i] == 'x')
        {
            F[i] = F[i - 1];
            L[i] = 0;
        }
        else
        {
            F[i] = F[i - 1] + L[i - 1] + 0.5;
            L[i] = (L[i - 1] + 1) / 2;
        }
    }
    printf("%.4Lf\n", F[n]);
    
	return 0;
}

P1654 OSU!(期望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 = 1e5 + 10;
double F[N], L[N], T[N], p[N];
int n;

int main()
{
    scanf("%d", &n);
    _for(i, 1, n) scanf("%lf", &p[i]);

    _for(i, 1, n)
    {
        F[i] = (F[i - 1] + 3 * T[i - 1] + 3 * L[i - 1] + 1) * p[i] + F[i - 1] * (1 - p[i]);
        L[i] = (L[i - 1] + 1) * p[i];
        T[i] = (T[i - 1] + 2 * L[i - 1] + 1) * p[i];
    }
    printf("%.1f\n", F[n]);
    
	return 0;
}

P6046 纯粹容器(期望+组合数学)

这题和dp没啥关系,很数学的一道题

显然对于一个容器,答案是\sum f_i(i-1) 

fi表示第i轮被干掉的概率 但是这个不好求,转化为前缀和

f_i=p_i-p_{i-1}

pi即前i轮被干掉的概率,这个好求,用古典概型求。

分两种情况求,一种是被左边第一个大于它的干掉,一个是右边。

把操作看作是在一条链上选择边,这样就有n-1条边去选,如果被左边的干掉,那么l~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;

typedef long long ll;
const int N = 50 + 10;
const int mod = 998244353;
ll c[N][N], p[N];
int n, a[N];

ll binpow(ll a, ll b)
{
    ll res = 1;
    for(; b; b >>= 1)
    {
        if(b & 1) res = res * a % mod;
        a = a * a % mod;
    }
    return res;
}

ll inv(ll x) { return binpow(x, mod - 2); }

int main()
{
    _for(i, 0, 50) c[i][0] = 1;
    _for(i, 1, 50)
        _for(j, 1, i)
            c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;

    scanf("%d", &n);
    _for(i, 1, n) scanf("%d", &a[i]);
    _for(i, 1, n)
    {
        int l = -1, r = -1;
        for(int j = i - 1; j >= 1; j--)
            if(a[j] > a[i])
            {
                l = j;
                break;
            }
        _for(j, i + 1, n)
            if(a[j] > a[i])
            {
                r = j;
                break;
            }
        if(l == -1 && r == -1)
        {
            printf("%d ", n - 1);
            continue;
        }
        
        _for(j, 1, n - 1)
        {
            ll pa = 0, pb = 0, pab = 0;
            if(l != -1 && j >= (i - l)) pa = c[n - 1 - (i - l)][j - (i - l)] * inv(c[n - 1][j]) % mod;
            if(r != -1 && j >= (r - i)) pb = c[n - 1 - (r - i)][j - (r - i)] * inv(c[n - 1][j]) % mod;
            if(l != -1 && r != -1 && j >= (i - l) + (r - i)) pab = c[n - 1 - (r - i) - (i - l)][j - (r - i) - (i - l)] * inv(c[n - 1][j]) % mod;
            p[j] = (pa + pb - pab + mod) % mod;
        }
        
        ll ans = 0;
        _for(j, 1, n - 1)
            ans = (ans + (p[j] - p[j - 1]) * (j - 1)) % mod;
        printf("%lld ", (ans + mod) % mod);
    }

	return 0;
}

周四

CF280C Game on Tree(期望)

看作一个长度为n的操作序列

操作看作染黑,一个点一染黑,就把它的子孙都染黑

那么一个点被操作的前提是它的祖先都在它后面

因此概率为 1 / dep[u]

操作次数的期望等于每个点操作的期望之和

每个点操作的期望就等于概率

#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 = 1e5 + 10;
vector<int> g[N];
int d[N], n;

void dfs(int u, int fa)
{
    d[u] = d[fa] + 1;
    for(int v: g[u])
    {
        if(v == fa) continue;
        dfs(v, u);
    }
}

int main()
{
    scanf("%d", &n);
    _for(i, 1, n - 1)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(1, 0);

    double ans = 0;
    _for(i, 1, n)
        ans += 1.0 / d[i];
    printf("%.10f\n", ans);

	return 0;
}

A. Little Pony and Expected Maximum(期望)

和前面一道题的思路类似

E=\sum f_i*i fi表示最大值为i的概率

等于某一值的概率不好算,要转化成小于等于某一值的概率,这样就可以用古典概型

f_i=p_i-p_{i-1}

p_i=\left ( i/m \right )^n

#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 = 1e5 + 10;
long double p[N];
int n, m;

long double binpow(long double a, int b)
{
    long double res = 1;
    for(; b; b >>= 1)
    {
        if(b & 1) res *= a;
        a *= a;
    }
    return res;
}

int main()
{
    scanf("%d%d", &m, &n);
    _for(i, 1, m) p[i] = binpow(1.0 * i / m, n);

    long double ans = 0;
    _for(i, 1, m)
        ans += (p[i] - p[i - 1]) * i;
    printf("%.10Lf\n", ans);

	return 0;
}

SP1026 FAVDICE - Favorite Dice(期望)

之前做过差不多一样的题,用dp做的。还有一种纯数学的方法

如果现在有k个图案,拿到一个新的图案的概率为(n - k) / n

有一个结论是,概率为p的事件发生所需要的期望次数是1/p

因此此时要n / (n / k)期望次数才能发生

这样枚举k,就可得到期望发生了多少次才得到全部答案

周五

P4550 收集邮票(期望)

做这种期望题除了用期望dp,即设一个dp状态表示已经……还需要多少的期望。这题同样可以用这种思路。

但还一种思路就是考虑每一次对答案的贡献,对于这道题这种思路特别简单

如果已经有了k种,再增加一种的概率是(n - k) / n

那么此时需要购买的期望次数为n / (n - k)

那么枚举k从0到n-1,考虑每一次对答案的贡献

对于次数就是当前的期望次数n / (n - k)

对于价格,就是期望次数的前缀和,也就是当前是第几次购买

#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 = 1e5 + 10;
double p[N];
int n;

int main()
{
    scanf("%d", &n);
    _for(i, 0, n - 1) p[i] = 1.0 * n / (n - i);

    double ans = 0, sum = 0;
    _for(i, 0, n - 1)
    {
        sum += p[i];
        ans += p[i] * sum;
    }
    printf("%.2f\n", ans);

	return 0;
}

CodeForces 547B(单调栈)

考虑一个数的贡献,即当前数在多长的区间中可以作为最小值。这个可以用单调栈O(n)来实现。

然后再把每个数的贡献合并起来即可。

#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;
int l[N], r[N], s[N], a[N], top, n;
int t[N], ans[N];
 
int main()
{
    scanf("%d", &n);
    _for(i, 1, n) scanf("%d", &a[i]);

    for(int i = n; i >= 1; i--)
    {
        while(top && a[s[top]] >= a[i]) top--;
        r[i] = top ? s[top] : n + 1;
        s[++top] = i;
    }
    top = 0;
    _for(i, 1, n)
    {
        while(top && a[s[top]] >= a[i]) top--;
        l[i] = top ? s[top] : 0;
        s[++top] = i;
    }

    _for(i, 1, n)
    {
        int len = r[i] - l[i] - 1;
        t[len] = max(t[len], a[i]);
    }
    ans[n] = t[n];
    for(int i = n - 1; i >= 1; i--)
        ans[i] = max(ans[i + 1], t[i]);
    _for(i, 1, n) printf("%d ", ans[i]);
 
	return 0;
}

CodeForces 1540A(思维)

关键是发现点的顺序是不影响的,所以直接排序,然后贪心即可

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

typedef long long ll; 
const int N = 1e5 + 10;
int a[N], n;

int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        _for(i, 1, n) scanf("%d", &a[i]);
        sort(a + 1, a + n + 1);

        ll ans = 0, sum = 0;
        _for(i, 2, n) ans += a[i] - a[i - 1];

        sum = a[n];
        for(int i = n - 1; i >= 1; i--)
        {
            ans += 1LL * (n - i) * a[i] - sum;
            sum += a[i];
        }
        printf("%lld\n", ans);
    }

	return 0;
}

AtCoder - agc006_b (思维)

中间放x-1 x x+1 剩下随便放

思路很巧妙

#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;
int ans[N];
 
int main()
{
	int n, x;
	scanf("%d%d", &n, &x);
	if(x == 1 || x == 2 * n - 1) 
	{
		puts("No");
		return 0;
	}
	
	ans[n - 1] = x - 1;
	ans[n] = x;
	ans[n + 1] = x + 1;
	
	int now = 0;
	_for(i, 1, 2 * n - 1)
	{
		if(ans[i]) continue;
		now++;
		if(now == x - 1) now = x + 2;
		ans[i] = now;
	}
	
	puts("Yes");
	_for(i, 1, 2 * n - 1) printf("%d\n", ans[i]);   
	
	return 0;
}

周六

hdu 2460(tarjan+lca)

首先tarjan可以得到一颗生成树,割边一定是生成树上的边,因为这道题只考虑割边,所以得到割边后,非生成树上的边就不用考虑了。

每次加边显然会把路径上的割边去掉,直接暴力的话计算一下是1e8,是可以过的,因此直接暴力就行。怎么保存割边呢,用k[u]表示u到u的父亲的这条边是否是割边

#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#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 = 1e5 + 10;
int dfn[N], low[N], k[N], d[N], f[N], cnt, n, m, sum;
vector<int> g[N];

void dfs(int u, int fa)
{
    d[u] = d[fa] + 1;
    f[u] = fa;
    dfn[u] = low[u] = ++cnt;
    bool vis = false;
    for(int v: g[u])
    {
        if(!dfn[v])
        {
            dfs(v, u);
            low[u] = min(low[u], low[v]);
            if(low[v] > dfn[u]) k[v] = 1, sum++;
        }
        else 
        {
            if(v == fa && !vis) vis = true;
            else low[u] = min(low[u], dfn[v]);
        }
    }
}

int main()
{
    int kase = 0;
    while(scanf("%d%d", &n, &m))
    {
        if(n == 0 && m == 0) break;
        printf("Case %d:\n", ++kase);

        _for(i, 1, n) g[i].clear(), dfn[i] = low[i] = k[i] = f[i] = 0;
        cnt = sum = 0;

        while(m--)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        dfs(1, 0);

      //  printf("sum: %d\n", sum);
       
        int q; scanf("%d", &q);
        while(q--)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            if(d[u] < d[v]) swap(u, v);
            while(d[u] > d[v])
            {
                if(k[u]) k[u] = 0, sum--;
                u = f[u];
            }
            while(u != v)
            {
                if(k[u]) k[u] = 0, sum--;
                u = f[u];
                if(k[v]) k[v] = 0, sum--;
                v = f[v];
            }
            printf("%d\n", sum);
        }
        puts("");
    }

	return 0;
}

hdu 7158(同余)

可以得到M是pq-1的因子,暴力枚举因子找到满足条件的即

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

typedef long long ll;
ll p, q, e;

bool check(ll x)
{
    if(x <= max(p, q)) return false;
    for(int i = 2; 1LL * i * i <= x; i++)
        if(x % i == 0)
            return false;
    return true;
}

int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        scanf("%lld%lld%lld", &p, &q, &e);
        ll t = p * q - 1, m = -1;
        for(int i = 1; 1LL * i * i <= t; i++)
            if(t % i == 0)
            {
                if(check(i)) 
                {
                    m = i;
                    break;
                }
                if(check(t / i))
                {
                    m = t / i;
                    break;
                }
            }
        if(m == -1) puts("shuanQ");
        else printf("%lld\n", e * q % m);
    }
   
	return 0;
}

周日

hdu5222(拓扑排序+并查集)

我一开始的思路不是正解的思路,是直接dfs,但是不知道为啥mle了,算一下空间是没超的,之前有一道hdu的题也是这样……

正解是先考虑无向边,用并查集,如果没有成环的话,那么接下来就缩点,就是一个联通分量缩成一个点,然后图就是有向图了,那么由向图判断有无环就用拓扑排序即可

#include<cstdio>
#include<vector>
#include<algorithm>
#include<map>
#include<queue>
#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;
#pragma comment(linker, "/STACK:102400000,102400000") 

const int N = 1e6 + 10;
int f[N], d[N], n, m1, m2, ans;
vector<int> g[N];

int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); }

int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d%d", &n, &m1, &m2);
        _for(i, 1, n) f[i] = i, d[i] = 0, g[i].clear();
        ans = 0;

        while(m1--)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            int fu = find(u), fv = find(v);
            if(fu == fv) ans = 1;
            else f[fu] = fv;
        }
        while(m2--)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            u = find(u); v = find(v);
            g[u].push_back(v);
            d[v]++;
        }

        queue<int> q;
        vector<int> topo;
        _for(i, 1, n)
            if(!d[i])
                q.push(i);
        while(!q.empty())
        {
            int u = q.front(); q.pop();
            topo.push_back(u);
            for(int v: g[u])
                if(--d[v] == 0)
                    q.push(v);
        }

        if(topo.size() != n) ans = 1;
        puts(ans ? "YES" : "NO");
    }

	return 0;
}

AtCoder - arc134_c(插板法)

首先非1的球随便放,每种是独立的,它放就是n个球放到k个盒子中,盒子不相同,可以有空,这就是典型的插板法了,即C(n + k - 1, k - 1) 

放完之后,先在每个盒子中,有多少球就放多少1,然后即剩下的1为n,那么就是将n个球放到k个盒子中,盒子不相同,每个至少有一个,即C(n - 1, k - 1)

无解的话,组合数那里n<m的时候返回0即可

关于组合数的计算,由于C(n, m)的m很小,所以可以直接暴力计算。

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

typedef long long ll;
const int mod = 998244353;
const int N = 1e5 + 10;
ll f[N], a[N];
int n, k;

ll binpow(ll a, ll b)
{
    ll res = 1;
    for(; b; b >>= 1)
    {
        if(b & 1) res = res * a % mod;
        a = a * a % mod;
    }
    return res;
}

ll inv(ll x) { return binpow(x, mod - 2); }

ll C(ll n, ll m) 
{ 
    if(n < m) return 0;
    ll res = 1;
    _for(i, n - m + 1, n) res = res * i % mod;
    return res * inv(f[m]) % mod;
}

int main()
{
    f[0] = 1;
    _for(i, 1, 200) f[i] = f[i - 1] * i % mod;

    scanf("%d%d", &n, &k);
    _for(i, 1, n) scanf("%lld", &a[i]);

    ll ans = 1, sum = 0;
    _for(i, 2, n)
    {
        ans = ans * C(a[i] + k - 1, k - 1) % mod;
        sum += a[i];
    }
    ans = ans * C(a[1] - sum - 1, k - 1) % mod;
    printf("%lld\n", ans);

	return 0;
}

D. Round Subset(背包)

这题的难点是想到把一个作为体积,一个作为价值跑背包

还是做的题太少

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

typedef long long ll;
const int N = 200 + 10;
int f[N * 30][N], w[N], v[N], sum, n, K;

int main()
{
    scanf("%d%d", &n, &K);
    _for(i, 1, n)
    {
        ll x; scanf("%lld", &x);
        while(x % 5 == 0)
        {
            x /= 5;
            w[i]++;
        }
        while(x % 2 == 0)
        {
            x /= 2;
            v[i]++;
        }
        sum += w[i];
    }

    //注意不合法的状态初始化为最小值
    memset(f, -0x3f, sizeof f);
    f[0][0] = 0;
    _for(i, 1, n)
        for(int j = sum; j >= w[i]; j--)
            for(int k = K; k >= 1; k--)
                f[j][k] = max(f[j][k], f[j - w[i]][k - 1] + v[i]);
    
    int ans = 0;
    _for(i, 0, sum)
        ans = max(ans, min(f[i][K], i));
    printf("%d\n", ans);

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值