[AHOI2017/HNOI2017]大佬

正解: \(dp\) + \(map\) + \(bfs\)

首先看到这个神题,第一感觉就是不可做,但是经过长时间的理解读题之后,发现这个题目还是很好理解的,根据题目的意思下 暴搜 XIN队算法也是很好打出的

首先考虑部分 \(40pts\)

我们设置递归函数 xin_team()

之后在设置所需要的变量:

day self him f l cnt

分别代表:现在的时间,自己的自信值,大佬的自信值,自己的讽刺值,现在的等级和使用讽刺的次数

理论上复杂度为 \(\mathcal O(5^n)\),实际上要低的多。

所以可以得出 \(40pts\) 解法:

#include<cmath>
#include<queue>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
#define m(c,num) memset(c,num,sizeof c)
#define INF 0x7f7f7f7f
#define debug cout<<"debug"<<endl
#define freopen eat = freopen
#define scanf a14 = scanf
#define eps (1e-8)
namespace xin_io
{
	#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1++
	char buf[1<<20],*p1 = buf,*p2 = buf; FILE *eat; int a14;
	inline void openfile() {freopen("t.txt","r",stdin);}
	inline void delfile()  {freopen("o.txt","w",stdout);}
	inline int get()
	{
		int s = 0,f = 1;register char ch = gc();
		while(!isdigit(ch))
		{if(ch == '-') f = -1;ch = gc();}
		while(isdigit(ch))
		{s = s * 10 + ch - '0';	ch = gc();}
		return  s * f;
	}
}
using namespace xin_io;
static const int maxn = 1e3+10;
namespace xin
{
	int a[maxn],c,w[maxn],mc,m,n;
	bool ok;
	void dfs(int tim,int self,int him,int f,int l,int cnt)
	{
		if(ok)return ;
		if(!him) {ok = true; return;}
		self -= a[tim];
		if(tim > n) return;
		if(self < 0 or him < 0 or cnt > 2) return;
		dfs(tim+1,self,him-1,f,l,cnt);
		dfs(tim+1,min(self + w[tim],mc),him,f,l,cnt);
		dfs(tim+1,self,him,f,l+1,cnt);
		dfs(tim+1,self,him,f*l,l,cnt);
		if(cnt + 1 <= 2) 
		dfs(tim+1,self,him-f,1,0,cnt+1);
	}
	inline short main()
	{
	#ifndef ONLINE_JUDGE
		openfile();
	#endif
		n = get(); m = get(); mc = get();
		for(register int i=1;i<=n;++i) a[i] = get();
		for(register int i=1;i<=n;++i) w[i] = get();
		while(m--)
		{
			ok = 0;
			c = get();
			dfs(1,mc,c,1,0,0);
			if(ok) printf("1\n");
			else printf("0\n");
		}
		return 0;
	}
}
signed main() {return xin::main();}

接下来我们考虑正解:

对于我们真正需要的,其实就是要将大佬的自信值减少到 \(0\) ,所以,我们的 最佳战略 就可以分为两个步骤:

1.养精蓄锐

2.开始攻击

我们设置 \(f_{i,j}\) 数组来计算养精蓄锐的时间,可以有 \(dp\) 转移:

\[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-1,j},f_{i,min(j-a_i+w_i,mc)}); \]

之后可以进行 \(bfs\) 之后开始扫描一边,因为比较懒用了map没有去手写 \(hash\) 来判断重复,还不是因为不会写。

剩下的细节在代码里的英文注释里面都有,\(English\) 好的同学沾光了。

\(code\)

#include<cmath>
#include<queue>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
#define int long long
#define m(c,num) memset(c,num,sizeof c)
#define INF 0x7f7f7f7f
#define debug cout<<"debug"<<endl
#define freopen eat = freopen
#define scanf a14 = scanf
#define eps (1e-8)
namespace xin_io
{
	#define gc() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1++
	char buf[1<<20],*p1 = buf,*p2 = buf; FILE *eat; int a14;
	inline void openfile() {freopen("t.txt","r",stdin);} inline void outfile()  {freopen("o.txt","w",stdout);}
	inline int get()
	{	
		int s = 0,f = 1;register char ch = gc();
		while(!isdigit(ch))
		{if(ch == '-') f = -1;ch = gc();}
		while(isdigit(ch))
		{s = s * 10 + ch - '0';	ch = gc();}
		return  s * f;
	}
}
using namespace xin_io;
inline int max(int x,int y) {return x > y ? x : y;}
inline int min(int x,int y) {return x < y ? x : y;}
static const int maxn = 1e3+10,mod = 1e9+7;
namespace xin
{
	class xin_bfs{public:int d,f,l;xin_bfs(int x,int y,int z):d(x),f(y),l(z){}}; //this is for BFS to record state
	pair<int,int>p[maxn*10000];int tot = 0;
	queue <xin_bfs> q;
	int n,m,mc;
	int a[maxn],c[maxn],w[maxn];
	int f[maxn][maxn];
	map<pair<unsigned long long,int>,bool>hash;
	int maxx = -0x7f7f7f7f,day = maxx; //here is the max day and the max c
	void bfs() //this bfs is for f and day
	{
		q.push(xin_bfs(1,1,0));
		while(q.size())
		{
			xin_bfs u = q.front(); q.pop();
			if(u.d == day) continue; //if now is equal to max day ,continue
			q.push(xin_bfs(u.d+1,u.f,u.l+1)); //here is going to bfs the next point
			if(u.l > 1 and u.f * u.l <= maxx and !hash[make_pair(u.f * u.l,u.d + 1)])
			{
				q.push(xin_bfs(u.d + 1, u.f*u.l , u.l));
				p[++tot] = make_pair(u.f * u.l,u.d+1);
				hash[make_pair(u.f * u.l,u.d + 1)] = 1;
			}
		}
		hash.clear();
	}
	inline short main()
	{
	#ifndef ONLINE_JUDGE
		openfile();
	#endif
		n = get(); m = get(); mc = get();
		for(register int i=1;i<=n;++i) a[i] = get();
		for(register int i=1;i<=n;++i) w[i] = get();
		for(register int i=1;i<=m;++i) c[i] = get(),maxx = max(maxx,c[i]);
		for(register int i=1;i<=n;++i)   //for here I am going to get f[i][j] for(i for day and j for self),and f[i][j] represent the max day I am going to hit darklao
			for(register int j=a[i];j<=mc;++j)
				f[i][j-a[i]] = max(f[i][j-a[i]],f[i-1][j] + 1), //nothing to do
				f[i][min(j-a[i]+w[i],mc)] = max(f[i-1][j],f[i][min(j-a[i]+w[i],mc)]); //there is that I do so many water problems	
		for(register int i=1;i<=n;++i)
			for(register int j=1;j<=mc;++j)
				day = max(day,f[i][j]);   //calc the maxday
		bfs();
		sort(&p[1],&p[tot+1]);
		for(register int i=1;i<=m;++i)
		{
			if(c[i] <= day) {printf("1\n"); continue;}
			bool ok = false;
			int mm = 0x7f7f7f7f7f7f7f7f;
			for(register int j=tot,k = 1;j;--j)
			{
				while(k < tot and p[k].first + p[j].first <= c[i])
					mm = min(mm,p[k].second - p[k].first),++k;
				if(mm + c[i] - p[j].first <= day - p[j].second) {ok = true; break;}
				if(p[j].first <= c[i] and c[i] - p[j].first <= day - p[j].second) {ok = true; break;}
			}
			printf("%d\n",ok);
		}
		return 0;
	}
}
signed main() {return xin::main();}
posted @ 2021-06-06 18:58  NP2Z  阅读(72)  评论(0编辑  收藏  举报