cfRound933div3-EFG题解

E-Rudolf and k Bridges

题意:

选择的桥在连续的行中,每个桥的支架安装位置是可以不一样的.

做法:赛时也感觉也感觉是dp,但是害怕dp,就选择了逃避.往贪心方向想,认为每次到了每个跳板都要跳到最远距离,实际上这样是不行的.很明显,可能存在近一点的点花费更少。

实际上是dp,而且也不难。首先容易想到,需要一个前缀和,记录每个一行最少的花费的前缀和.然后枚举选择的连续的区间就好了。问题是每一行的最少花费怎么算。这其实是一个简单dp问题,对于一个普遍的点,他可能从他的前d+1个点来的,很明显,在前d+1个点中选花费最小那个就好了.如果每次都遍历前d+1个点, 找到到达那个点的最小值的话,这样复杂度很高。这个时候关键来了!用一个优先队列(小根堆)存放所有经历过的点的花费和下标,如果堆顶的点离现在的点超过d+1,直接pop.那么此时只要存在堆里的,都是合法范围的,而且堆顶必然是花费最小的,即从堆顶那个点跳到当前这个点即是最优的.然后把dp[i][m]加到前缀和,之后枚举区间,更新ans即可.

void solve(){               //E  跳板--是不对,到了某个点之后,不一定要跳尽,才是最好的;;
    // 求每一行最小的代价(dp)+前缀和
    //优先队列优化dp!!不用优先队列的话,因为每次需要前面合法的格子的最小代价是多少,需要遍历,这个时候就适合用优先队列,不用遍历去找最小代价.
    int n,m,k,d;   //n行,m列,建连续k桥,最大支架间隔d
    cin>>n>>m>>k>>d;
    vector<vector<int>> arr(n+1,vector<int>(m+1));
    vector<vector<int>> dp(n+1,vector<int>(m+1,0));
    int pre[105]={0};
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>arr[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> pq;   //小根堆
        dp[i][1]=1,pq.emplace(1,1);
        for(int j=2;j<=m;j++){
            while(pq.size()&&pq.top().second+d+1<j) pq.pop();
            dp[i][j]=arr[i][j]+1+pq.top().first;
            pq.emplace(dp[i][j],j);
        }
    }
    for(int i=1;i<=n;i++) pre[i]=pre[i-1]+dp[i][m];
    //int ans=INT_MAX;            //INT_MAX不够大!!!!如果没有看错误样例,这里又要找错找很久很久都找不出来。。。经典错误!!
    int ans=LONG_LONG_MAX;
    for(int i=k;i<=n;i++) ans=min(ans,pre[i]-pre[i-k]);
    cout<<ans<<endl;
}

ps!!ans要初始化为LONG_LONG_MAX,因为INT_MAX不够大.经典细节.

F-Rudolf and Imbalance

题意:

即选择i,j后将di+fj插入a数组中,使得a数组的最大差值最小.

做法:记录a数组中的第一大间距dif1和次大间距dif2.只有插入的数值在第一大间距的区间[L,R]中,才有可能使答案更小。所以可以枚举数组b,对于每个b[i],在c数组二分找最接近(L+R)/2减b[i]的值,即第一个大于的值,和第一个小于的值。如果位置合法,就更新答案。

int a[100005],b[200005],c[200005];
int n,m,k,ll,rr,mmid,dif1=INT_MIN,dif2=INT_MIN;
void solve(){                   //F--可能有m*k种不同的值要在a[n]上二分..m*k种组合已经T爆了,2e5*2e5..
    //第一,读清晰题目--求最大间隔的最小值。
    //找到最大的间隔dif1,及产生dif1的L,R.和次大的间隔dif2.(可能是一样的,如果是一样的可以直接输出,因为无论怎么插入,都改变不了最大间隔的最小值)--还有n==2的情况是没有dif2的,但是我的代码好像不用特判这个也能跑
    //现在目标是找到bi+cj在[L,R]区间,而且尽可能靠近mid=(L+R)/2,这样可以最大限度减少间隔dif1.
    //那么可以枚举b,在c上二分找靠近mmid的bi+cj..那么a的大小是唬人的,实际上只需要直到dif1-[L,R]和dif2。
    //怎么找靠近mmid的bi+cj?是在mmid在左边还是右边?---!!不要自己写的二分,用upper_bound(mmid-b[i])和prev(upper_bound(mmid-b[i]))
    cin>>n>>m>>k;
    dif1=INT_MIN,dif2=INT_MIN;
    for(int i=1;i<=n;i++) {
        cin>>a[i];
        if(i>=2){
            if(a[i]-a[i-1]>=dif1){
                if(dif1!=INT_MIN) dif2=dif1;
                dif1=a[i]-a[i-1];
                ll=a[i-1],rr=a[i];
                mmid=(ll+rr)/2;
            }
            else if(a[i]-a[i-1]>dif2) dif2=a[i]-a[i-1];
        }
    }
    for(int i=1;i<=m;i++) cin>>b[i];
    for(int i=1;i<=k;i++) cin>>c[i];
//    sort(b+1,b+m+1);
    sort(c+1,c+k+1);
    int ans=dif1,check0=0;
    for(int i=1;i<=m;i++){
//        if(b[i]>=mmid) break;    //..不要提前退出。。。
//    if(b[i]>=rr) break;  //或者大于右边界才提前退出
//否则wa:
//1
//7 2 5
//9 11 16 19 20 21 25  --mmid=13
//14 14             //可以找到15,插入15
//1 7 8 13 14
//        auto r=upper_bound(c+1,c+k+1,mmid-b[i]);
//        auto l=prev(r);
        int r=upper_bound(c+1,c+k+1,mmid-b[i])-c;   //是减c,不是减c+1
        int l=r-1;
        if(r!=k+1&&b[i]+c[r]>=ll&&b[i]+c[r]<=rr) ans=min(ans,max({dif2,rr-(b[i]+c[r]),(b[i]+c[r])-ll}));
        if(l>=1&&b[i]+c[l]>=ll&&b[i]+c[l]<=rr) ans=min(ans,max({dif2,rr-(b[i]+c[l]),(b[i]+c[l])-ll}));
    }
    cout<<ans<<endl;
}

G-Rudolf and Subway

 题意:

做法:

int n,m,s,t;
const int inf=0x3f3f3f3f;
priority_queue< pair<int,int> > pq;
void solve(){			//G!--巧妙的加虚点建边方法。--之后跑模板dijkstra
    //ps:保证给定的颜色是相连的,任意两个顶点最多只有一条边
    cin>>n>>m;
    vector<bool> vis(n+m+1,0);
    vector<int> dis(n+m+1,inf);
    vector<vector<pair<int,int>>> vct(n+m+1);
    map< pair<int,int> ,int> mp;            //避免重复建同一条边
    map<int,int> hash0;     //缩小n+c的值,避免初始化vct耗太多时间。
    // 一开始因为没有这个,导致初始化不彻底,因为c的值有点大--vct初始化1到n+m+1可能会不够,如果初始化到最大的n+c又太花时间
    int curhash=n+1;
    for(int i=1;i<=m;i++){
        int u,v,c;
        cin>>u>>v>>c;
        if(hash0[c]==0) {
            hash0[c]=curhash;
            curhash++;
        }
        if(!mp[{hash0[c],u}]) vct[hash0[c]].push_back({u,0});
        if(!mp[{hash0[c],v}]) vct[hash0[c]].push_back({v,0});
        if(!mp[{u,hash0[c]}]) vct[u].push_back({hash0[c],1});
        if(!mp[{v,hash0[c]}]) vct[v].push_back({hash0[c],1});
        mp[{hash0[c],u}]=1,mp[{hash0[c],v}]=1,mp[{u,hash0[c]}]=1,mp[{v,hash0[c]}]=1;
    }
    cin>>s>>t;
    //以下为dijkstra,写在solve里面,不用初始化工作
    dis[s]=0;
    pq.push({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[from]+weight<dis[to]){
                dis[to]=dis[from]+weight;
                pq.push({-dis[to],to});
            }
        }
    }
    cout<<dis[t]<<endl;
}
int n,m,s,t;
const int inf=0x3f3f3f3f;
priority_queue< pair<int,int> > pq;
bool vis[400005];
int dis[400005];
vector<pair<int,int>> vct[400005];
void dijkstra(int s){
    dis[s]=0;
    pq.push({0,s});
    while(pq.size()){
        int from=pq.top().second;
//        if(from==t) return;               //不能有这个return!!!!!!!!⚠️!
        pq.pop();
        if(vis[from]) continue;
        vis[from]=1;
        for(auto v:vct[from]){
            int to=v.first,weight=v.second;
            if(dis[from]+weight<dis[to]){
                dis[to]=dis[from]+weight;
                pq.push({-dis[to],to});
            }
        }
    }
}
void solve(){			//G!--巧妙的加虚点建边方法。--之后跑模板dijkstra
    //ps:保证给定的颜色是相连的,任意两个顶点最多只有一条边
    cin>>n>>m;
    map< pair<int,int> ,int> mp;            //避免重复建同一条边
    map<int,int> hash0;     //缩小n+c的值,避免初始化vct耗太多时间。
    // 一开始因为没有这个,导致初始化不彻底,因为c的值有点大--vct初始化1到n+m+1可能会不够,如果初始化到最大的n+c又太花时间
    int curhash=n+1;
    for(int i=1;i<=m;i++){
        int u,v,c;
        cin>>u>>v>>c;
        if(hash0[c]==0) {
            hash0[c]=curhash;
            curhash++;
        }
        vis[hash0[c]]=0,vis[u]=0,vis[v]=0;   //用到哪些点初始化哪些点
        dis[hash0[c]]=inf,dis[u]=inf,dis[v]=inf;
        if(!mp[{hash0[c],u}]) vct[hash0[c]].push_back({u,0});
        if(!mp[{hash0[c],v}]) vct[hash0[c]].push_back({v,0});
        if(!mp[{u,hash0[c]}]) vct[u].push_back({hash0[c],1});
        if(!mp[{v,hash0[c]}]) vct[v].push_back({hash0[c],1});
        mp[{hash0[c],u}]=1,mp[{hash0[c],v}]=1,mp[{u,hash0[c]}]=1,mp[{v,hash0[c]}]=1;
    }
    cin>>s>>t;
    dis[t]=inf;
    dijkstra(s); //初始化!
    cout<<dis[t]<<endl;
    for(int i=1;i<=n+m+1;i++) vct[i].clear();  //初始化
}

第一次补完全部题--谨此纪念

posted @ 2024-03-21 23:00  osir  阅读(14)  评论(0编辑  收藏  举报