2024SMU蓝桥训练1补题

B-航班时间

思路:地理知识--时差计算-东加西减。此处去程和返程方向相反,时差相加必然抵消。那么就可以知道实际飞行时间

ps:这题有点奇怪,本地跑不过样例,交上去是AC。本地跑过样例,交上去RE,WA。RE好像是因为输入的格式不够严格..

D-飞机降落

思路:全排列枚举 or dfs--dfs类似之前电科校赛没做出来的电阻题,当时不是很懂,现在知道怎么写这种需要全排列的dfs了。细节见代码注释。

typedef struct myp{
    int t,d,l;
};
void solve(){       //补D--飞机降落               !全排列枚举!--全排列枚举的确是可以的,但是想的不够!细!,情况考虑不周全
    int n; cin>>n;
    myp arr[n];
    int idx[n];
    for(int i=0;i<n;i++){
        cin>>arr[i].t>>arr[i].d>>arr[i].l;
        idx[i]=i;
    }
    bool ans=false;
    int times=1;
    for(int i=1;i<=n;i++) times*=i;  //全排列次数
    while(times--){
        bool check=true;
        int curtime=arr[idx[0]].t+arr[idx[0]].l;  //这次全排列的第一架飞机达到马上起飞..
        for(int i=1;i<n;i++){
            if(curtime>=arr[idx[i]].t&&curtime<=arr[idx[i]].t+arr[idx[i]].d) curtime+=arr[idx[i]].l;
            else if(curtime<arr[idx[i]].t) curtime=arr[idx[i]].t+arr[idx[i]].l;  //是curtime=,不是curtime+=
            //主要是以上两个条件,一开始还只有一个if,只是判了curtime<=arr[idx[i]].t+arr[idx[i]].d
            //看了题解之后加了else if:curtime<arr[idx[i].t] 飞机还没来,不能起飞,来了才能起飞
            //如果是飞机还没来的情况,那么curtime是直接被更新为arr[idx[i]].t+arr[idx[i]].l--而不是+=
            else{
                check=false;
                break;
            }
        }
        if(check){
            ans=true;
            break;
        }
        next_permutation(idx,idx+n);
    }
    if(ans) cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
}
typedef struct myp{
    int t,d,l;
}myp;
myp arr[15];
int n,ans=0,vis[15];
void dfs(int step,int curtime){
    if(step==n+1){
        ans=1;
        return;
    }
    if(ans) return;
    int curtime0=curtime;
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            if(curtime<=arr[i].t) {
                vis[i]=1;
                curtime=arr[i].t+arr[i].l;
                dfs(step+1,curtime);
                curtime=curtime0;
                vis[i]=0;
            }
            else if(curtime>arr[i].t&&curtime<=arr[i].t+arr[i].d) {
                vis[i]=1;
                curtime+=arr[i].l;
                dfs(step+1,curtime);
                curtime-=arr[i].l;
                vis[i]=0;
            }
            else return;
        }
    }
}
void solve(){               //补D--飞机降落 ---dfs暴搜,类似电阻题--step是当前选的物品数目,而不是当前选的物品编号!!!
    cin>>n;
    ans=0;
    for(int i=1;i<=n;i++) cin>>arr[i].t>>arr[i].d>>arr[i].l;
    for(int i=1;i<=n;i++) vis[i]=0;
    dfs(1,-1);
    if(ans) cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
}

F-调手表

思路:一开始写的类似递推的东西。不对的。正解:建有向边跑最短路即可。

const int inf=0x3f3f3f3f;
vector<pair<int,int>> vct[100005];
priority_queue<pair<int,int>> pq;
int vis[100005],dis[100005];
void dijkstra(int s){
    memset(vis,0,sizeof(vis));
    memset(dis,inf,sizeof(dis));
    dis[s]=0;
    pq.emplace(0,s);
    while(pq.size()){
        int from=pq.top().second;
        pq.pop();
        if(vis[from]) continue;
        vis[from]=1;
        for(auto v:vct[from]){
            int to=v.first,weight=v.second;
            if(dis[to]>dis[from]+weight){
                dis[to]=dis[from]+weight;
                pq.emplace(-dis[to],to);
            }
        }
    }
}
void solve(){               //补F-调手表----建图跑dijkstra最短路,输出最长路.
    //简单地想,每个点会被3条边连着,最多也只是3e5条边,可以跑图
    //有向图。
    int n,k; cin>>n>>k;
    for(int i=0;i<n;i++){
        if(i+1<n) vct[i].emplace_back(i+1,1);
        //if(i+1<n) vct[i+1].emplace_back(i,1);
        vct[i].emplace_back((i+k)%n,1);
        //vct[(i+k)%n].emplace_back(i,1);
    }
    dijkstra(0);
    int maxn=0;
    for(int i=0;i<n;i++) maxn=max(maxn,dis[i]);
    cout<<maxn;
}

G-更小的数

思路:这题数据水,暴力o(n^3)也能过。但是得写正解--简单的区间dp。见代码注释

//初始全为0
int dp[5005][5005];   //定义为区间[i,j]是否是合法的
//ai>aj:dp[i][j]=1;
//ai<aj:dp[i][j]=0;
//ai==aj:dp[i][j]=dp[i+1][j-1];
//因为需要dp[i+1][j-1]的值,所以i要从大到小枚举!!!
void solve(){       //补G--更小的数--正解--dp
    string str; cin>>str;
    int n=str.size(),ans=0;
    for(int i=n-1;i>=0;i--){
        for(int j=i;j<n;j++){
            if(str[i]>str[j]) dp[i][j]=1;
            else if(str[i]<str[j]) dp[i][j]=0;
            else if(str[i]==str[j]) dp[i][j]=dp[i+1][j-1];   //i==j的情况dp[i+1][j-1]永远是0
            if(dp[i][j]) ans++;
        }
    }
    cout<<ans;
}

H-青蛙过河

思路:二分+前缀和查询--详细见代码注释。

int n,x,ans;
int arr[100005],pre[100005];
bool check(int jump){
    for(int i=jump;i<=n-1;i++){         //一旦某一个区间不能容纳2x个青蛙,当前jump是不行的
        if(pre[i]-pre[i-jump]<x) return false;
    }
    return true;
}
void solve(){           //补H--青蛙过河--二分+前缀和
    //二分容易想到,问题是check函数怎么写?
    //首先可以简化一点点题意
    //x趟来回,因为来回是没有区别对。显然是可以当作从0到n,2*x次
    //也可以理解为2*x个青蛙要从0到n一次
    //刚开始,所有青蛙都跳出一步,此时2*x只青蛙均在区间[1,jump]中。
    //接下来所有青蛙跳到下一个区间[2,jump+1]中,其中处于1的青蛙要找[2,jump+1]区间中找空闲的位置跳。
    //意思是对于每一个jump长度的区间都要有2*x的位置容纳所有青蛙,否则不可能到达对岸.
    //以此类推,一直跳到区间[n-jump,n-1]..在这个区间的所有青蛙下一步都可以跳到终点.
    cin>>n>>x;
    x*=2;
    for(int i=1;i<=n-1;i++){
        cin>>arr[i];
        pre[i]=pre[i-1]+arr[i];
    }
    int l=1,r=n;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid)){
            ans=mid;
            r=mid-1;
        }
        else l=mid+1;
    }
    cout<<ans;
}

 

posted @ 2024-04-04 11:28  osir  阅读(4)  评论(0编辑  收藏  举报