2020.10.22 模擬賽題解
注:所有題目並非作者版權,也並非本人原創。本人只是為了方便大家除錯,將校內的題面與資料利用平臺做成題目,並對題面進行部分美化。特此說明。
房間開燈(\(\text{lightson}\))
簡要題意:略。
彩蛋:我們老師說這道題可以做 \(TG \space T1\),爺笑了。
做法很顯然是寬搜,但是有細節。因為有回頭路。
比方說你 \((1,1) \rightarrow (1,2) \rightarrow (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\n",i,j)*/; printf("%d\n",ans); return 0; }
水果盛宴(feast)
簡要題意:略。
顯然,\(T \leq 5 \times 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\) 為奇數的時候也可以喝水,\(x\) 變為原來的 \(\lfloor \frac{x}{2} \rfloor\),下取整。原題題意不明確,導致本人出錯,特此說明,題面中也有標註。
另外有個技巧:可以不必開 \(f[1 - 5 \times 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\n",ans);
return 0;
}