zoukankan      html  css  js  c++  java
  • [AH2017/HNOI2017]大佬 (DP+搜索+two-pointers)

    题面

    LOJ传送门

    题解

    每天的操作有很多种,但是实际上可以分为两大类。

    第一类只与自己的自信值有关:刷水题/被大佬嘲讽。

    第二类与大佬的自信值有关:L+=1、F乘等于L、还嘴、怼大佬

    那么对于第一类,我们只要保证把大佬怼死前自信值非负就行了。而要保证自信值非负可以通过DP来求最多留出多少天来进行第二类操作。

    假设求出来最多留出(D)天来搞事情,问题就转化为一共只有(D)天,每天可以L+=1、F乘等于L、还嘴、怼大佬。

    然后可以通过暴力(BFS)求出花了(d)天能够怼掉大佬(f)点自信值的((d,f))数对,搜索的时候需要减枝((F)超过最大的(C_i)就不继续了)。

    求出来后按(f)为第一关键字,(d)为第二关键字排序。

    由于最多可以怼两次,怼一次或者不怼很显然。怼两次要求的就是是否存在((d1,f1),(d2,f2))满足(f1+f2le C)(f1+f2+(D-d1-d2)ge C)

    我们从后往前枚举(i),设指针(j)满足(f_i+f_jle C)且最大。那么固定(i)的话,(f1,D,d1,C)都是定值,此时只需要求(f2-d2)的最大值就行了。直接存一下就行了。由于显然的单调性,(j)的位置一定是递增的。所以直接用一个变量存(j)就行了。

    CODE

    #include <bits/stdc++.h>
    using namespace std;
    typedef pair<int,int> pii;
    typedef long long LL;
    const int MAXN = 105;
    const int MAXM = 25;
    int n, m, mc, D, a[MAXN], w[MAXN], C[MAXM], mxc;
    int f[MAXN][MAXN];
    int predp() {
    	f[0][mc] = 0;
    	for(int i = 1; i <= n; ++i)
    		for(int j = a[i]; j <= mc; ++j) {
    			f[i][j-a[i]] = max(f[i][j-a[i]], f[i-1][j]+1);
    			f[i][min(j-a[i]+w[i],mc)] = max(f[i][min(j-a[i]+w[i],mc)], f[i-1][j]);
    		}
    	int re = 0;
    	for(int i = 1; i <= n; ++i) //注意这里不能只求f[n][i](i=1...mc)的最大值,因为可以没到第n天就把大佬怼死了
    		for(int j = 0; j <= mc; ++j)
    			re = max(re, f[i][j]);
    	return re;
    }
    struct node {
    	int x; pii y;
    };
    queue<node>myq;
    map<pii, bool>vis;
    pii val[2000005];
    int cnt;
    void BFS() {
    	myq.push((node){ 1, pii(1, 0) });
    	while(!myq.empty()) {
    		int d = myq.front().x, F = myq.front().y.first, L = myq.front().y.second; myq.pop();
    		if(d >= D) break;
    		myq.push((node){ d+1, pii(F, L+1) });
    		if(L > 1 && 1ll*F*L <= mxc && !vis.count(pii(F*L, d+1))) {
    		//这里判重我写的map 结果loj过了 luogu上过不了,必须手写hash表才能过
    			myq.push((node){ d+1, pii(F*L, L) });
    			vis[pii(F*L, d+1)] = 1;
    			val[++cnt] = pii(F*L, d+1);
    		}
    	}
    }
    int main () {
    	scanf("%d%d%d", &n, &m, &mc);
    	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    	for(int i = 1; i <= n; ++i) scanf("%d", &w[i]);
    	for(int i = 1; i <= m; ++i) scanf("%d", &C[i]), mxc = max(mxc, C[i]);
    	D = predp(); BFS(); sort(val + 1, val + cnt + 1);
    	for(int i = 1; i <= m; ++i) {
    		if(C[i] <= D) { puts("1"); continue; } //不怼
    		bool flg = 0; int mx = -0x3f3f3f3f;
    		for(int j = cnt, k = 1; j >= 1; --j) { //固定j 移动k
    			while(k <= cnt && val[k].first + val[j].first <= C[i]) {
    				mx = max(mx, val[k].first-val[k].second);
    				++k;
    			}
    			if(val[j].first-val[j].second+D+mx >= C[i]) { flg = 1; break; } //怼两次
    			if(val[j].first <= C[i] && val[j].first+D-val[j].second >= C[i]) { flg = 1; break; } //怼一次
    		}
    		printf("%d
    ", flg);
    	}
    }
    
  • 相关阅读:
    python压平嵌套列表
    利用Python通过频谱分析和KNN完成iphone拨号的语音识别
    教你用Python Jupyter Notebook 制作代码分享 PPT
    windows10远程桌面连接身份验证错误:函数不受支持,这可能是由于 CredSSP 加密 Oracle 修正
    常用Linux命令
    利用uWSGI和nginx进行服务器部署
    阿里云ECS Ubuntu16.0 安装 uwsgi 失败解决方案
    阿里云上安装pip3(Ubuntu)
    基于python的快速傅里叶变换FFT(二)
    基于Python的频谱分析(一)
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12093716.html
Copyright © 2011-2022 走看看