洛谷 P3724 大佬 —— 决策dp + BFS

题目链接:点我啊╭(╯^╰)╮

题目大意:

     中文题

解题思路:

    发现自己的自信值是否足够和能否打败大佬是两码事
    因此可以先用一个 D P DP DP 求出最多可以腾出多少天可以来蓄力,设最多有 D D D
    然后接下来肯定是用一个 D P DP DP 求出最终答案,但是这个 D P DP DP 用什么来最为转移的数据完全不知道
    那么假设有一个 p a i r : ( d , f ) pair:(d, f) pair(d,f),表示用 d d d 天,可以造成 f f f 的攻击
    也就是说如果知道了所有的 p a i r pair pair,就可以进行 d p dp dp 了(虽然还不知道怎么 d p dp dp
    很明显,可以通过 B F S BFS BFS 来求出所有 p a i r pair pair ,那么如何计算时间复杂度呢?
    可以猜测必须通过这些 p a i r pair pair 进行 d p dp dp ,那么这些 p a i r pair pair 的数量一定不会太大
    所以可以直接 B F S BFS BFS 就完事了!注意要去重


    如果最多只能攻击一次,则只需满足 f ≤ c f ≤ c fc    & & \&\& &&   f + D − d ≥ c f + D - d ≥ c f+Ddc 即可
    如果能攻击两次,设对应的两个 p a i r pair pair ( d 1 , f 1 ) , ( d 2 , f 2 ) (d1, f1) ,(d2, f2) (d1,f1)(d2,f2)
     f 1 + f 2 ≤ c f1 + f2 ≤ c f1+f2c    & & \&\& &&   f 1 + f 2 + ( D − d 1 − d 2 ) ≥ c f1 + f2 + (D - d1 - d2) ≥ c f1+f2+(Dd1d2)c
    暴力枚举肯定不行, 那么思考是否能满足决策优化
    对 f f f 进行从小到大的排序,则随着 f 1 f1 f1 的增大, f 2 f2 f2 一直减少
    但如果这样枚举会产生不合法的 p a i r pair pair,只需要对 f 1 f1 f1 从大到小枚举即可
    特别注意要特判单独的 f 1 f1 f1 是否满足 以及 c ≤ D c ≤ D cD 的情况

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const int maxn = 105;
int n, m, mc, a[maxn], w[maxn], c[maxn]; 
int D, dp[maxn][maxn], mxc, top;
map <pii, int> mp;
struct node{
	int d, f, l;
	bool operator < (const node A) const {
		if(f == A.f) return d < A.d;
		return f < A.f;
	}
} st[2000005];

int getD(){
	for(int i=1; i<=n; i++)
		for(int j=a[i]; j<=mc; j++){
			dp[i][j-a[i]] = max(dp[i][j-a[i]], dp[i-1][j] + 1);
			int nj = min(mc, j - a[i] + w[i]);
			dp[i][nj] = max(dp[i][nj], dp[i-1][j]);
		}
	int ret = 0;
	for(int i=1; i<=n; i++) 
		for(int j=0; j<=mc; j++)
			ret = max(ret, dp[i][j]);
	return ret;
}

void bfs(){
	queue <node> q;
	node u = {1, 1, 0}, v;
	q.push(u);
	while(q.size()){
		u = q.front(), q.pop();
		if(u.d >= D) continue;
		q.push({u.d + 1, u.f, u.l + 1});
		if(u.l <= 1 || 1ll * u.f * u.l > 1ll * mxc) continue;
		if(mp[{u.d + 1, u.f * u.l}]) continue;
		v.d = u.d + 1, v.f = u.f * u.l, v.l = u.l;
		q.push(v), mp[{u.d + 1, u.f * u.l}] = 1;
		st[++top] = {u.d + 1, u.f * u.l};
	}
}

signed 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 = getD();
	bfs();
	sort(st+1, st+1+top);
	for(int i=1; i<=m; i++){
		int f = 0, mx = -1e9;
		if(c[i] <= D) {
			printf("%d\n", 1);
			continue;
		}
		for(int f1=top, f2=1; f1; f1--){
			while(f2 < top && st[f1].f + st[f2].f <= c[i]) 
				mx = max(mx, st[f2].f - st[f2].d), f2++;
			if(st[f1].f+D-st[f1].d+mx >= c[i]) {
				f = 1;
				break;
			}
			if(st[f1].f<=c[i] && st[f1].f-st[f1].d+D >= c[i]) {
				f = 1;
				break;
			}
		}
		printf("%d\n", f);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值