[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();}