前面的碎碎念:
状态好差,罚时上天,我裂开了QAQ。

题解部分:

A-古老的牛市,遗迹的天梯
典型的BFS搜索题,关键在于设好状态。设标记数组为V[i][j],表示以连续后退了j步的状态访问第i层阶梯,那么开始BFS搜索,对于某个状态,按照题目给定的要求,其可以退一步,或者跳跃到与其高度差不超过2^k的阶梯上,每次都按照这两点转移,最后输出到第n阶阶梯的最小步数即可。
时间复杂度:O(n图片说明log(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(300图片说明300)。
代码部分:

#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(n图片说明log(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的特殊情况。
时间复杂度:(n图片说明m)。
代码部分:

#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]);
}

完结撒花,如有疏漏之处,还望轻喷。