2019-8-12 考试总结
A. 引子
大模拟。
先标记水箱的位置,然后顺着每条水管走,最后$dfs$就可以了。
丑陋的代码:
#include<iostream> #include<cstring> #include<string> #include<cstdio> #include<vector> #define int long long #define Maxn 1050 #define Reg register #define _max(x,y) ((x)>(y)?(x):(y)) using namespace std; int n,m,cnt,tot,num[Maxn][Maxn]; int Lx[Maxn*Maxn],Rx[Maxn*Maxn],Ly[Maxn*Maxn],Ry[Maxn*Maxn],stack[Maxn*Maxn]; char s[Maxn][Maxn]; vector<vector<int> > son(Maxn*Maxn); int find(int x,int y,int k) { if(x<1||x>n||y<1||y>m) return -1; if(num[x][y]) return num[x][y]; else if(s[x][y]=='|') { s[x][y]='.'; if(s[x+1][y]=='|'||s[x+1][y]=='+'||(num[x+1][y]&&num[x+1][y]!=k)) return find(x+1,y,k); else if(s[x-1][y]=='|'||s[x-1][y]=='+'||(num[x-1][y]&&num[x-1][y]!=k)) return find(x-1,y,k); } else if(s[x][y]=='-') { s[x][y]='.'; if(s[x][y+1]=='-'||s[x][y+1]=='+'||(num[x][y+1]&&num[x][y+1]!=k)) return find(x,y+1,k); else if(s[x][y-1]=='-'||s[x][y-1]=='+'||(num[x][y-1]&&num[x][y-1]!=k)) return find(x,y-1,k); } else if(s[x][y]=='+') { s[x][y]='.'; if(s[x+1][y]=='|'||s[x+1][y]=='+'||(num[x+1][y]&&num[x+1][y]!=k)) return find(x+1,y,k); else if(s[x-1][y]=='|'||s[x-1][y]=='+'||(num[x-1][y]&&num[x-1][y]!=k)) return find(x-1,y,k); else if(s[x][y+1]=='-'||s[x][y+1]=='+'||(num[x][y+1]&&num[x][y+1]!=k)) return find(x,y+1,k); else if(s[x][y-1]=='-'||s[x][y-1]=='+'||(num[x][y-1]&&num[x][y-1]!=k)) return find(x,y-1,k); } return -1; } void dfs(int x) { for(Reg int i=0;i<son[x].size();++i) dfs(son[x][i]); stack[++stack[0]]=x; return; } signed main() { scanf("%lld%lld",&n,&m); for(Reg int i=0;i<=n+1;++i) for(Reg int j=0;j<=m+1;++j) s[i][j]='.'; for(Reg int i=1;i<=n;++i) scanf("%s",s[i]+1); for(Reg int i=1;i<=n;++i) { for(Reg int j=1;j<=m;++j) { if(s[i][j]=='.') continue; else if(s[i][j]=='+') { int lnow=j,rnow=j+1,unow=i,dnow=i+1,ok=1; while(rnow<m&&s[i][rnow]=='-') ++rnow; while(dnow<n&&s[dnow][j]=='|') ++dnow; if(rnow>m||dnow>n) continue; for(Reg int k=lnow+1;k<=rnow-1;++k) if(s[dnow][k]!='-') {ok=0; break;} for(Reg int k=unow+1;k<=dnow-1;++k) if(s[k][rnow]!='|') {ok=0; break;} if(!ok) continue; if(s[i][rnow]=='+'&&s[dnow][j]=='+'&&s[dnow][rnow]=='+') { int po=0; for(Reg int k=unow;k<=dnow;++k) { for(Reg int p=lnow;p<=rnow;++p) { if(s[k][p]>='0'&&s[k][p]<='9') po=po*10+s[k][p]-'0'; s[k][p]='.'; } } if(!po) continue; for(Reg int k=unow;k<=dnow;++k) for(Reg int p=lnow;p<=rnow;++p) num[k][p]=po; cnt=_max(cnt,po); Lx[po]=unow,Rx[po]=dnow,Ly[po]=lnow,Ry[po]=rnow; } } } } for(Reg int i=1;i<=cnt;++i) { for(Reg int j=Rx[i];j>=Lx[i];--j) { if(s[j][Ly[i]-1]=='-') { int p=find(j,Ly[i]-1,i); if(p!=-1) son[i].push_back(p); } if(s[j][Ry[i]+1]=='-') { int p=find(j,Ry[i]+1,i); if(p!=-1) son[i].push_back(p); } } } dfs(1); for(Reg int i=1;i<=stack[0];++i) printf("%lld\n",stack[i]); return 0; }
B. 可爱精灵宝贝
乍一看这个题像一个很简单的$dp$,
如果精灵不消失的话,那么就很简单了。
但是精灵被抓住后会消失,这就有点难了。
正解:
用$dp[t][l][r][0/1]$表示状态,在第$t$秒左侧拓展到$l$,右侧拓展到$r$,现在在左端点$(0)/$在右端点$(1)$的最大收益。
为什么可以这样。
显然,要抓到一个精灵必须经过这个精灵到原点$k$之间的点,
而如果这个点已经走过,那么再走肯定不会有收益。
只要先把所有的精灵按照位置排序,然后枚举时间、左右端点就可以了。
下面是转移:
好吧很麻烦,看代码。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #define Maxn 2050 #define Reg register #define INF 0x7fffff #define _max(x,y) ((x)>(y)?(x):(y)) #define _abs(x) ((x)<0?(-1*(x)):(x)) using namespace std; int n,k,m,mxx=0,ans=0,tot=0,mid,dp[Maxn][150][150][2]; struct Node {int a,b,t;} Q[Maxn]; bool comp(Node a,Node b) {return a.a<b.a;} int main() { scanf("%d%d%d",&n,&k,&m); Q[++tot].a=k,Q[tot].b=0,Q[tot].t=0; for(Reg int i=1,a,b,t;i<=m;++i) { scanf("%d%d%d",&a,&b,&t); if(a==k) Q[1].b+=b; else { Q[++tot].a=a,Q[tot].b=b,Q[tot].t=t; mxx=_max(mxx,Q[tot].t); } } sort(Q+1,Q+tot+1,comp); for(Reg int i=1;i<=tot;++i) if(Q[i].a==k) mid=i; for(Reg int i=1;i<=mxx;++i) for(Reg int j=mid;j>=1;--j) for(Reg int p=mid;p<=tot;++p) dp[i][j][p][0]=dp[i][j][p][1]=-INF; dp[1][mid][mid][0]=dp[1][mid][mid][1]=Q[mid].b; for(Reg int i=1;i<=mxx;++i) { for(Reg int j=mid;j>=1;--j) { for(Reg int p=mid,sum;p<=tot;++p) { if(p+1<=tot&&i+Q[p+1].a-Q[j].a<=mxx) { sum=dp[i][j][p][0]; if(Q[p+1].t>=i+Q[p+1].a-Q[j].a) sum+=Q[p+1].b; dp[i+Q[p+1].a-Q[j].a][j][p+1][1]=_max(dp[i+Q[p+1].a-Q[j].a][j][p+1][1],sum); } if(j-1>=1&&i+Q[j].a-Q[j-1].a<=mxx) { sum=dp[i][j][p][0]; if(Q[j-1].t>=i+Q[j].a-Q[j-1].a) sum+=Q[j-1].b; dp[i+Q[j].a-Q[j-1].a][j-1][p][0]=_max(dp[i+Q[j].a-Q[j-1].a][j-1][p][0],sum); } if(j-1>=1&&i+Q[p].a-Q[j-1].a<=mxx) { sum=dp[i][j][p][1]; if(Q[j-1].t>=i+Q[p].a-Q[j-1].a) sum+=Q[j-1].b; dp[i+Q[p].a-Q[j-1].a][j-1][p][0]=_max(dp[i+Q[p].a-Q[j-1].a][j-1][p][0],sum); } if(p+1<=tot&&i+Q[p+1].a-Q[p].a<=mxx) { sum=dp[i][j][p][1]; if(Q[p+1].t>=i+Q[p+1].a-Q[p].a) sum+=Q[p+1].b; dp[i+Q[p+1].a-Q[p].a][j][p+1][1]=_max(dp[i+Q[p+1].a-Q[p].a][j][p+1][1],sum); } ans=_max(ans,_max(dp[i][j][p][0],dp[i][j][p][1])); } } } printf("%d",ans); return 0; }
C. 相互再归的鹅妈妈
总结:
$T1$是个大模拟,
数组开小$+$判水箱判错让我丢了$20$分,拿到$80$,
$T1$大概码了不到一个小时,然后去看$T2$。
$T2$看起来很简单,题目很好懂,
一开始很自然地想到二维$dp$,但很快被$Hack$掉。
观察题目性质,然后这个正解便很好想。
$T2$码了一个多小时,然后又调了很长时间。
最后剩下$T3$,好吧,容斥?组合数?一堆数学的东西。
看起来很像按位转移的$dp$,好像没多少时间了。
还是输出样例吧。。。
总的来说心态很好(尤其是看到$T1$是个大模拟),
$T2$能好好想了。
时间分配,$T2$时间有点长,总的来说应该还行。
好像给$T3$时间多了也拿不了多少分。。。
不能想当然,很容易拿到$WA$。
最后$80+100+10=190$
没什么水平。。。