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;
}