HGOI7.7集训题解

题解

今天这把题有点简单,除了第二题数学方法没想到之外,剩下的250草草收场。

第一题——音量调节(changingsounds)

【题目描述】

  • 一个**DJ要玩调音,给了beginlevel和maxlevel,又给了若干个音量调整,从beginlevel开始,从1-n可加可减,不能超过0-maxlevel的限制,问你到第n首调完最大音量是多少。

  • 我一看卧槽这不是妥妥的广搜题么!然后我抄起键盘,复制粘贴就是干!五分钟打完广搜。
    这里写图片描述
  • 然后我一看数据, n 50 ,那广搜怕是要爆,果断改变策略。
  • 想了想,可以当做dp题来做。设个布尔数组,从1-n之间枚举,判断是否为合法情况。
    方程如下

    i f ( j + c [ i ] m a x l e v e l ) d p [ i ] [ j ] = d p [ i ] [ j ] | | d p [ i 1 ] [ j + c [ i ] ] ;

    i f ( j c [ i ] 0 ) d p [ i ] [ j ] = d p [ i ] [ j ] | | d p [ i 1 ] [ j c [ i ] ] ;

  • 然后扫一遍最后的n就可以得到最大值。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
using namespace std;
void fff(){
    freopen("changingsounds.in","r",stdin);
    freopen("changingsounds.out","w",stdout);
}
const int MAXN=55;
int begin_level,max_level,n;
int c[MAXN];
bool dp[MAXN][1010];
int ans=0;
int main(){
    fff();
    scanf("%d%d%d",&n,&begin_level,&max_level);
    for (int i=1;i<=n;i++){
        scanf("%d",&c[i]);
    }
    dp[0][begin_level]=true;
    for (int i=1;i<=n;i++){
        for (int j=max_level;j>=0;j--){
            if(j+c[i]<=max_level){
                dp[i][j]=dp[i][j]||dp[i-1][j+c[i]];
            }
            if(j-c[i]>=0){
                dp[i][j]=dp[i][j]||dp[i-1][j-c[i]];
            }
        }
    }
    for (int i=max_level;i>=0;i--){
        if(dp[n][i]){
            cout<<i;
            return 0;
        }
    }
    cout<<-1;
    return 0;
}

第二题——旅行(journey)

【题目描述】

  • 就是给你一张图,X是挡住路径的但不会封死路径,. 是可通过路径,数据给定保证每行每列最多有1个X,而50%的数据是不含有X的。让你求随机起点到随机终点的平均路径。
  • 公式:(假设有p个点)
    i , j = 1 p | x i x j | + | y i y j | p 2

  • 你会发现其实你可以骗50分,而且数据也不大,只要枚举每一行,从这一行开始往后扫一遍就可以了。根据可逆性节省一半时间orz,五十分轻松到手。
  • 对于剩下的50分,你会发现,如果是按照公式就是直接走,但会有X挡路,所有需要越过X,而每越过一个X,你的距离就会增加2。由于没有连续的对角线挡路,所以只要考虑多少对 < i , j > 被X挡住了。
  • 但是很不幸,我刚开始只想到了在同一行、列内如果挡住了会有需要绕过X的情况,但其实如果不同的两行、列之间,所走的曼哈顿路径也被X挡住了,那也是需要进行绕路的,这个没想到ojz
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#define LL long long
using namespace std;
void fff(){
    freopen("journey.in","r",stdin);
    freopen("journey.out","w",stdout);
}
const int MAXN=1010;
int a[MAXN][MAXN];
double ans=0,l[MAXN],h[MAXN],h1[MAXN],l1[MAXN],tot,n,m;

int main(){
    fff();
    scanf("%lf%lf\n",&n,&m);
    for (LL i=1;i<=n;i++){
        h[i]=214748347;
    }
    for (LL i=1;i<=m;i++){
        l[i]=214748347;
    }
    for (LL i=1;i<=n;i++){
        for (LL j=1;j<=m;j++){
            char c;
            scanf("%c",&c);
            if(c=='X'){
                h[i]=j;
                l[j]=i;
                a[i][j]=1;
            }else{
                a[i][j]=1;
                tot++;
                h1[i]++;
                l1[j]++;
            }
        }
        if(i!=n) scanf("\n");
    }
    for (LL i=1;i<=n;i++){
        for (LL j=i;j<=n;j++){
            ans+=(h1[i]*h1[j]*(j-i)*2.0)/tot/tot;
        }
    }
    for (LL i=1;i<=m;i++){
        for (LL j=i;j<=m;j++){
            ans+=(l1[i]*l1[j]*(j-i)*2.0)/tot/tot;//记录行、 列之间.的乘机和,有对称可逆性可以减少复杂度 
        }
    }
    for (LL i=1;i<=m;i++){
        if(l[i]<214748347){
            double jy=l[i]-1;
            for (int j=i;j>1&&l[j-1]<l[j];j--) jy+=l[j-1]-1;//就是那个被挡住曼哈顿路径的也需要进行增加,而增加的点数就是l[j-1]到l[j]之间的点数了,无限被挡orz
            for (int j=i;j<m&&l[j+1]<l[j];j++) jy+=l[j+1]-1;
            ans+=4.0*(n-l[i])*jy/tot/tot;//对称可逆,越过一个x要加2,那么对称就要加4 
        }
    }
    for (LL i=1;i<=n;i++){
        if(h[i]<214748347){
            double jy=h[i]-1;
            for (int j=i;j>1&&h[j-1]<h[j];j--) jy+=h[j-1]-1;
            for (int j=i;j<n&&h[j+1]<h[j];j++) jy+=h[j+1]-1;
            ans+=4.0*(m-h[i])*jy/tot/tot;//最后再来算一把就ok了
        }
    }
    printf("%.4lf\n",ans);
    return 0;
}

第三题——舞蹈课(dancinglessons)

【题目描述】
让你模拟跳舞的场景。在队列当中是异性的组合,默契差最小的出队,而剩下的人自动补上位置,知道不能再出队列。

这里写图片描述

  • 一看到默契差最小,我就知道要用堆来维护这个最小值。剩下的工作就是预处理和维护合法情况。十五分钟就打完了。一遍就AC。手感是真的棒。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>

using namespace std;
void fff(){
    freopen("dancinglessons.in","r",stdin);
    freopen("dancinglessons.out","w",stdout);
}
int n;
const int MAXN=200010;
struct node{
    bool is_gay,is_out;
    int l,r;
    int val;
    node(){
        is_out=false;
    }
}a[MAXN];
int _abs(int a){
    if(a<0) a=-a;
    return a;
}
struct min_pair{
    int delta;
    int l,r;
    bool operator <(const min_pair x) const{
        if(delta==x.delta) return l>x.l;
        return delta>x.delta;
    }
};
int k=0;
struct Ans{
    int l,r;
}ans[MAXN];
priority_queue <min_pair> heap;

char s[MAXN];
int main(){
    fff();
    scanf("%d",&n);
    cin>>s;
    a[1].l=0;
    a[n].r=n+1;
    for (int i=1;i<=n;i++){
        scanf("%d",&a[i].val);
        if(s[i-1]=='B') a[i].is_gay=true;//看这个人是不是男的
            else a[i].is_gay=false;
        if(i!=1) a[i].l=i-1;
        if(i!=n) a[i].r=i+1;
        if(a[i].is_gay!=a[i-1].is_gay&&i!=1){
            heap.push((min_pair){_abs(a[i].val-a[i-1].val),i-1,i});//先把相邻的合法的进堆
        }
    }
    while (!heap.empty()){//堆维护最小值
        min_pair e=heap.top(); heap.pop();
        #define LF a[e.l].l
        #define RF a[e.r].r
        if(!a[e.l].is_out&&!a[e.r].is_out&&e.l!=0&&e.r<=n){//防止出现一个人已经出去了另一个人还在堆里
            ans[++k]=(Ans){e.l,e.r};
            a[e.l].is_out=true;
            a[e.r].is_out=true;
            a[LF].r=a[e.r].r;
            a[RF].l=a[e.l].l;//指针迁移
            if(a[LF].is_gay!=a[RF].is_gay){
                heap.push((min_pair){_abs(a[LF].val-a[RF].val),LF,RF});//继续进堆
            }
        }
    }
    cout<<k<<'\n';
    for (int i=1;i<=k;i++){
        printf("%d %d\n",ans[i].l,ans[i].r);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42037034/article/details/80951042