前面的碎碎念:
状态好差,罚时上天,我裂开了QAQ。
题解部分:
A-古老的牛市,遗迹的天梯
典型的BFS搜索题,关键在于设好状态。设标记数组为V[i][j],表示以连续后退了j步的状态访问第i层阶梯,那么开始BFS搜索,对于某个状态,按照题目给定的要求,其可以退一步,或者跳跃到与其高度差不超过2^k的阶梯上,每次都按照这两点转移,最后输出到第n阶阶梯的最小步数即可。
时间复杂度:O(nlog(max(ai)))。
代码部分:
#include<bits/stdc++.h> using namespace std; struct node { int x,h,s; }Q[6005]; bool flag=0,V[205][35]={0}; int main() { int i,j,n,x,h,s,r=1,f=0,a[205]; scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d",&a[i]); Q[0].x=1,Q[0].h=Q[0].s=0,V[1][0]=1; while(r!=f) { x=Q[f].x,h=Q[f].h,s=Q[f++].s; if(x==n) { flag=1; break; } for(i=x+1;i<=n&&a[i]-a[x]<=((long long)1<<s);i++) { if(V[i][0])continue; V[i][0]=1,Q[r].x=i,Q[r].s=0,Q[r++].h=h+1; } if(x>1&&s<31&&!V[x-1][s+1])V[x-1][s+1]=1,Q[r].x=x-1,Q[r].s=s+1,Q[r++].h=h+1; } if(!flag)h=-1; printf("%d\n",h); return 0; }
B-几乎毁灭牛市的流星雨
还是BFS搜索,我们用一个数组T,先初始化为无限大,然后预处理出地图上每个格子被陨石砸中的最短时间。那么我们从(0,0)点开始BFS,找走到任意一个时间无限大点的最短时间就行了。注意虽然数据范围给的x=300,y=300,但是实际上人可以走到300以外的地方,我因此WA了一发QAQ。
时间复杂度:O(300300)。
代码部分:
#include<bits/stdc++.h> using namespace std; struct node { int x,y,h; }Q[100005]; int T[305][305],dx[]={0,0,1,-1},dy[]={1,-1,0,0}; bool flag=0,V[305][305]={0}; int main() { int i,j,r=1,f=0,x,y,h,X,Y,n; scanf("%d",&n); for(i=0;i<=303;i++) for(j=0;j<=303;j++)T[i][j]=1e9; while(n--) { scanf("%d%d%d",&x,&y,&h); T[x][y]=min(T[x][y],h); for(i=0;i<4;i++) { X=x+dx[i],Y=y+dy[i]; if(X<0||X>303||Y<0||Y>303)continue; T[X][Y]=min(T[X][Y],h); } } Q[0].x=Q[0].y=Q[0].h=0,V[0][0]=1; while(r!=f) { x=Q[f].x,y=Q[f].y,h=Q[f++].h; if(T[x][y]==1e9) { flag=1; break; } for(i=0;i<4;i++) { X=x+dx[i],Y=y+dy[i]; if(X<0||X>303||Y<0||Y>303||h+1>=T[X][Y]||V[X][Y])continue; V[X][Y]=1,Q[r].x=X,Q[r].y=Y,Q[r++].h=h+1; } } if(!flag)h=-1; printf("%d\n",h); return 0; }
C-迁徙过程中的河流
上来一看感觉是个贪心题,首先一个很直白的贪心思想就是:每次让最短时间的人和另一个人渡河,然后最短时间的人再划船回来,不断运人过去,直到最后都到达了对岸。为了方便讲解,我们对渡河时间数组a排序后,a[0]是最短渡河时间,a[1]是第二短渡河时间,设s=a[0]+a[1]+...+a[n-1],那么以上思想的总渡河时间就是a[0](n-2)+s-a[0]。
但是很明显,这样连样例都过不了,因为这样取得的解不一定是最优的...通过思考(其实是观察样例),我们可以发现,可以让a[0]减少1个,a[n-2]减少1个,然后a[1]增加2个;然后可以继续a[0]减少1个,a[n-4]减少1个,a[1]增加2个;继续a[0]减少1个,a[n-6]减少1个,a[1]增加2个,以此类推...所以我们保存这个递推过程中,最小的那个总渡河时间就行了。
时间复杂度:O(nlog(n))。
代码部分:
#include<bits/stdc++.h> using namespace std; long long a[100005]; int main() { long long i,j,s=0,n,ans; scanf("%lld",&n); for(i=0;i<n;i++)scanf("%lld",&a[i]),s+=a[i]; sort(a,a+n); if(n==1){printf("%lld\n",s);return 0;} j=ans=a[0]*(n-2)+s-a[0]; for(i=4;i<=n;i+=2)j=j-a[0]+2*a[1]-a[n-i+2],ans=min(ans,j); printf("%lld\n",ans); return 0; }
D-牛牛去牛市旅游
第一眼看过去以为是个状压DP,但是复杂度明显不行,于是想着是不是要折半枚举,但是没想出来,结果实际上是个数学问题。
对于必须要通过的道路,我们对其两结点之间连边,那么设其最终有x个连通分量,其中有y个连通分量只有1个结点,那么有x-y个连通分量的节点数是大于1的。对于任意一个节点数大于1的连通分量,要使其合法,必须满足以下两个条件:
1:任意一个结点的度数要小于等于2。
2:不可以有环(此点包括我在内的很多人都没有注意)。
因为每个节点数大于1的连通分量若合法,其可以有正反2种排列,因此最终的答案即为x!2^(x-y)。
时间复杂度:O(n^2)。
代码部分:
#include<bits/stdc++.h> using namespace std; const int mod=1e9+7; vector<int>R[55]; bool V[55]={0}; void DFS(int x,int fa) { int i,j; if(R[x].size()>2){printf("0\n");exit(0);} for(i=0;i<R[x].size();i++) { j=R[x][i]; if(j==fa)continue; if(V[j]){printf("0\n");exit(0);} V[j]=1,DFS(j,x); } } int main() { int i,j,x=0,y=0,n,A[55]; char T[55][55]; scanf("%d",&n); for(A[0]=i=1;i<=n;i++)scanf("%s",T[i]+1),A[i]=(long long)A[i-1]*i%mod; for(i=1;i<=n;i++) for(j=1;j<=n;j++)if(T[i][j]=='Y')R[i].push_back(j); for(i=1;i<=n;i++)if(!V[i])x++,V[i]=1,DFS(i,0); for(i=1;i<=n;i++)if(!R[i].size())y++; printf("%d\n",((long long)1<<(x-y))%mod*A[x]%mod); }
E-牛牛的旅游纪念品
此题做的我心态炸裂,狂WA6发,最后把数组改成long long就A了,我吐了。题面说答案在int范围内,可能中间结果溢出了吧,唉...
我们设DP[i][j]为:前j个商品取i个,在满足题目的位置差条件下的最大欢迎度之和,那么我们有转移方程:
for(i=2;i<=m;i++) for(j=max(k+1,i);j<=n;j++)DP[i][j]=max(DP[i][j-1],DP[i-1][j-k]+a[j]);
因为第一个商品可以任取,所以特殊处理DP[1][i]就行了,同时也要注意k为0的特殊情况。
时间复杂度:(nm)。
代码部分:
#include<bits/stdc++.h> using namespace std; long long a[10005],DP[105][10005]; int main() { int i,j,n,m,k; scanf("%d%d%d",&n,&m,&k); if(!k)k=1; memset(DP,-0x3f,sizeof(DP)); for(i=1;i<=n;i++)scanf("%lld",&a[i]),DP[1][i]=max(DP[1][i-1],a[i]); for(i=2;i<=m;i++) for(j=max(k+1,i);j<=n;j++)DP[i][j]=max(DP[i][j-1],DP[i-1][j-k]+a[j]); printf("%lld\n",DP[m][n]); }
完结撒花,如有疏漏之处,还望轻喷。