注:所有题目并非作者版权,也并非本人原创。本人只是为了方便大家调试,将校内的题面与数据利用平台做成题目,并对题面进行部分美化。特此说明。
房间开灯(( ext{lightson}))
简要题意:略。
彩蛋:我们老师说这道题可以做 (TG space T1),爷笑了。
做法很显然是宽搜,但是有细节。因为有回头路。
比方说你 ((1,1) ightarrow (1,2) ightarrow (1,3)),发现 ((1,3)) 把 ((2,1)) 的灯开了,这时候你就要倒回去才能走到 ((2,1)),进行下一步操作。
那你可能会说,好,我把开灯的位置都走一遍呢?也不对。如果你开灯的房间是你走不到的呢?这怎么办?很显然我们需要一个数组来记录 当前房间的门有没有被打开。被打开的话,之后被开灯就可以直接入队搜索;未被打开的话,之后被开灯也无益。最后统计答案。
这样思路很明显了,但是代码有细节。
时间复杂度:(mathcal{O}(n^2 + m)).(数据出水了)
#include<bits/stdc++.h>
using namespace std;
const int N=1e2+1;
inline int read(){char ch=getchar(); int f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
int x=0; while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}
inline void write(int x) {
if(x<0) {putchar('-');write(-x);return;}
if(x<10) {putchar(char(x%10+'0'));return;}
write(x/10);putchar(char(x%10+'0'));
}
const int dx[4]={-1,0,0,1};
const int dy[4]={0,-1,1,0};
int n,m,ans=0;
vector<pair<int,int> > a[N][N];
bool h[N][N],lit[N][N],door[N][N];
// h 记录队列是否搜索过这个元素
// lit 记录该房间的灯是否打开
// door 记录该房间的门是否打开
queue<pair<int,int> > q;
int main() {
// freopen("lightson.in","r",stdin);
// freopen("lightson.out","w",stdout);
n=read(),m=read();
while(m--) {
int x=read(),y=read(),u=read(),v=read();
a[x][y].push_back(make_pair(u,v));
} q.push(make_pair(1,1)); lit[1][1]=1; door[1][1]=1;
while(!q.empty()) {
int x=q.front().first,y=q.front().second;
q.pop(); if(h[x][y]) continue; h[x][y]=1;
for(register int i=0;i<a[x][y].size();i++) {
int nx=a[x][y][i].first,ny=a[x][y][i].second;
lit[nx][ny]=1;
if(door[nx][ny] && !h[nx][ny]) q.push(make_pair(nx,ny));
}
for(register int i=0;i<4;i++) {
int nx=x+dx[i],ny=y+dy[i];
if(nx<1 || ny<1 || nx>n || ny>n || h[nx][ny]) continue;
door[nx][ny]=1; if(lit[nx][ny]) q.push(make_pair(nx,ny));
}
}
for(register int i=1;i<=n;i++)
for(register int j=1;j<=n;j++) if(lit[i][j]) ans++/*,printf("%d %d
",i,j)*/;
printf("%d
",ans);
return 0;
}
水果盛宴(feast)
简要题意:略。
显然,(T leq 5 imes 10^6) 是个线性做法,如何动态规划?
一种思路是考虑 (f_i) 表示 (leq i) 的最大饱腹值(不喝水),最后考虑喝水的情况。则:
(f_i = max(f_{i-A} + A , f_{i-B} + B))
如何考虑喝水?则 (f_i) 与 (f_{i div 2}) 如何建立联系?只能喝一次水,难以解决问题。
这样我们考虑记忆化搜索,试图改变这个策略。现在我们的状态多一维,由于搜索的灵活性,我们的状态转移灵活了 (114514) 倍。
当前 (dfs(x,f)) 表示当前的饱腹值为 (x),是否喝过水((f=1) 喝过,(f=0) 未喝过),可以轻松转移。
注:这里有个坑。就是饱腹值 (x) 为奇数的时候也可以喝水,(x) 变为原来的 (lfloor frac{x}{2} floor),下取整。原题题意不明确,导致本人出错,特此说明,题面中也有标注。
另外有个技巧:可以不必开 (f[1 - 5 imes 10^6][2]) 的数组,一维哈希即可解决问题。答案的记录每次打擂即可解决!
时间复杂度:(mathcal{O}(T)).
#include<bits/stdc++.h>
using namespace std;
const int N=5e6+1;
inline int read(){char ch=getchar(); int f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
int x=0; while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}
inline void write(int x) {
if(x<0) {putchar('-');write(-x);return;}
if(x<10) {putchar(char(x%10+'0'));return;}
write(x/10);putchar(char(x%10+'0'));
}
int T,A,B,ans;
bool h[N];
inline void dfs(register int x,bool f) {
if(h[x]) return; ans=max(ans,x);
h[x]=1;
if(!f/* && !(x&1)*/) dfs(x>>1,1); // 喝一口水
if(x+A<=T) dfs(x+A,f); // 拿一个 A
if(x+B<=T) dfs(x+B,f); // 拿一个 B
}
int main() {
// freopen("feast.in","r",stdin);
// freopen("feast.out","w",stdout);
T=read(); A=read(); B=read();
dfs(0,0); printf("%d
",ans);
return 0;
}