[AH2017/HNOI2017]大佬 暴力BFS+双指针

44 篇文章 0 订阅
41 篇文章 1 订阅
博客主要介绍了如何利用动态规划解决在洛谷平台上的解题策略问题,通过五种操作策略分析,得出最大存活天数。之后,通过BFS与双指针技巧优化算法,求解在嘲讽值约束下能否击败所有对手。文章内容涉及动态规划、图论和算法优化技巧。
摘要由CSDN通过智能技术生成
题目大意:

洛谷

解题思路:
  • 五种操作中只有一种与自己的自信值有关,而其他四种的操作在第i天操作或第j天操作都一样(假设只操作一次),即这四种操作跟第几天无关
  • 所以可以先通过 d p dp dp来求出自己最大的存活天数,再在天数上面做操作
  • d p [ i ] [ j ] dp[i][j] dp[i][j]为第 i i i j j j自信的最大存活天数,则可以通过 O ( n 2 ) O(n^2) O(n2)即可求醉最大存活天数
  • 问题就转换为:给你n天,能否怼死大佬
  • 怼大佬只与天数和嘲讽值有关,那么先直接BFS+Map判重枚举出所有状态
  • 按照嘲讽值从大到小排序就行,要满足 f 1 + f 2 ≤ C , f 1 + f 2 + ( D − d 1 − d 2 ) ≥ C f_1+f_2\le C, f_1+f_2+(D-d_1-d_2)\ge C f1+f2C,f1+f2+(Dd1d2)C
  • 先固定 f i f_i fi,那么 f i , d i , D , C f_i,d_i,D,C fidi,D,C也知晓,只是要 f 2 − d 2 f_2-d_2 f2d2的最大值
  • 如果暴力的话就是 O ( 状 态 数 2 ) O(状态数^2) O(2),但是因为已经排好序,所以直接双指针头尾就行(具体可看代码注释)
AC代码:
#include <bits/stdc++.h>
#define ft first
#define sd second
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 1e2 + 10;
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
ll qpow(ll a, ll b) {
   ll res = 1;
   while (b) {
       if (b & 1) res = res * a % mod;
       a = a * a % mod;
       b >>= 1;
   }
   return res;
}
int n, m, mc, day, mx;
int a[maxn], w[maxn], c[maxn], dp[maxn][maxn];
struct Node {
    int i, f, l;
};
queue <Node> q;
map <pii, bool> vis;
int cntz;
pii zt[1000100];
void bfs() {
    q.push({1, 1, 0});
    while (!q.empty()) {
        Node u = q.front(); q.pop();
        if (u.i == day) continue;
        q.push({u.i + 1, u.f, u.l + 1}); //操作三
        if (u.l > 1 && 1ll * u.f * u.l <= 1ll * mx && !vis.count({u.f * u.l, u.i + 1})) {
            q.push({u.i + 1, u.f * u.l, u.l}); //操作四
            zt[++cntz] = {u.f * u.l, u.i + 1};
            vis[{u.f * u.l, u.i + 1}] = true;
        }
    }
}
int main() {
    IOS;
    cin >> n >> m >> mc;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) cin >> w[i];
    for (int i = 1; i <= m; i++) cin >> c[i], mx = max(mx, c[i]);
    for (int i = 1; i <= n; i++)
        for (int j = a[i]; j <= mc; j++) {
            dp[i][j - a[i]] = max(dp[i - 1][j] + 1, dp[i][j - a[i]]);
            dp[i][min(j - a[i] + w[i], mc)] = max(dp[i - 1][j], dp[i][min(j - a[i] + w[i], mc)]);
        }
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= mc; j++)
            day = max(day, dp[i][j]);
    bfs();
    sort (zt + 1, zt + cntz + 1);
    for (int i = 1; i <= m; i++) {
        if (c[i] <= day) {
            cout << 1 << endl;
            continue;
        }
        bool ok = false;
        int mmin = 1e9;
        // f1 + f2 <= C, f1 + f2 + (D - d1 - d2)(剩余天数用来操作一) >= C
        for (int j = cntz, k = 1; j >= 1; j--) { 
            while (k < cntz && zt[k].ft + zt[j].ft <= c[i]) //因为已经按照f为关键之排序,所以当j变小时,k符合的区间一定包含了之前的区间,即[1,k_j] \in [1, k_{j-1}],所以直接在之前状态下继续就行
                mmin = min(mmin, zt[k].sd - zt[k].ft), ++k; //f2 - d2的最大值,即d2 - f2的最小值
            if (zt[j].ft + day - zt[j].sd - mmin >= c[i]) {ok = true; break;} //看怼两次是否够
            if (zt[j].ft <= c[i] && c[i] - zt[j].ft <= day - zt[j].sd) {ok = true; break;} //看怼一次是否就够
        }
        ok ? cout << 1 << endl : cout << 0 << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值