HGOI7.8集训题解

题解

今天这个题目做的我心态有点崩,题目比昨天难好多orz

这里写图片描述


第一题——小明搬家(box)

【题目描述】

  • 就是告诉你有k个人在上下楼梯搬箱子,分别向上或者向下,两个人如果方向相反的相碰,那么两个人就交接箱子,然后反向。地面上还有m个箱子要搬,那么就来问你最少要多少时间。

  • 其实这道题比较好像,如果这两个人就当做两个点,你就可以认为,他们两个是相互越过了。所以其实并没有提高搬箱的效率。然后地上的箱子肯定是谁先下来谁来搬,看大家还要搬几趟,剩下的箱子就先下来的人来搬。
  • 那处理的时候就要看下谁先下来了。简单的数学题。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
void fff(){
    freopen("box.in","r",stdin);
    freopen("box.out","w",stdout);
}
const int MAXN=500100;
long long a[MAXN];

long long n,k,m;
int main(){
    fff();
    scanf("%lld%lld%lld",&n,&k,&m);
    for (int i=1;i<=k;i++){
        int b;
        scanf("%lld%d",&a[i],&b);
        if(b==0) a[i]=n-1+n-a[i];
        else a[i]=a[i-1];
    }
    sort(a+1,a+k+1);
    long long ans=(m/k-1)*(n-1)*2+n-1;
    if(m%k==0) ans+=a[k];
    else ans+=a[m%k];
    printf("%lld",ans);
    return 0;
}

第二题——圆圈舞蹈(circle)

【题目描述】

  • 告诉你有n头奶牛,练成了环,再告诉你相邻两头牛之间的距离,任意两头牛之间的距离是顺时针与逆时针距离的最小值。问你最大距离是多少。

  • 这道题最开始只想处暴力算法。大概能够骗个几十分的。
  • 后来想想不行,不能这么颓废。就果断的打了个二分。
  • 二分思想:对于任意的点,到另一个任意点的顺时针和逆时针的距离之和就是整个环的长度。那么求这个距离(顺逆最小)就只要让顺逆尽可能的相等或者说接近。那就枚举每一个节点再来二分顺逆的中间点来找到这个最接近的距离。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
void fff(){
    freopen("circle.in","r",stdin);
    freopen("circle.out","w",stdout);
}
const int MAXN=310000;
int _abs(int x){
    if(x<0) x=-x;
    return x;
}
int a[MAXN];//i~i+1'dist
int n,k,m;
int ans=0;
int s[MAXN];//1~i'dis_sum
int main(){
    fff();
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        a[i+n]=a[i];

    }
    for (int i=1;i<=2*n;i++){
        s[i+1]=s[i]+a[i];//前缀和预处理下
    }
    int g;
    for (int i=1;i<=n;i++){
        int l=i,r=i+n;
        int min=1<<31-1;
        int mid;
        while (l<=r){
            mid=(l+r)/2;
            int x=s[mid]-s[i];
            int y=s[i+n]-s[mid];
            if(_abs(x-y)<min){//二分接近最小值
                min=_abs(x-y);
                if(x<y){
                    g=x;
                }else{
                    g=y;
                }
                if(g>ans){//更优的解,那就更新
                    ans=g;
                }
            }
            if(l==mid) break;
            if(x<y){
                l=mid;
            }else{
                r=mid;
            }
        }
        int x=s[mid]-s[i];
        int y=s[i+n]-s[mid];
        if(_abs(x-y)<min){
            min=_abs(x-y);
            if(x<y){
                g=x;
            }else{
                g=y;
            }
            if(g>ans){
                ans=g;//出来以防万一
            }
        }
    }
    cout<<ans;
    return 0;
}

第三题——物流运输(trans)

【题目描述】

  • 告诉你一张图,m个点,一艘船在n天里要从1点出发到m点送货,每天所需要的运费是路径长度总和,每天都有可能会封掉若干个点。而你有可能需要更改路径,这会让你花费k的价值。让你求n天内的最小价值是多少。

  • 这道题一眼看过去就知道是dp+最短路。然后就想歪了orz。
  • 可以看出,如果第i天的原有路径被封住,那么你需要更改路径,而这一定会需要+k,那么怎么样去想dp方程呢?刚开始我们以为需要状压。(一把hash暴力教你做人),后来想想不对,一个港口从第i~j天被封住,那我只要求出任意两天之间被封住的港口全部被封住的最短路拿来dp就好了嘛!方程么..

dp[i]=(LL)ww[1][i]*i

dp[i]=min(dp[i],dp[j]+k+(LL)ww[j+1][i]*(i-j))

  • 如果1-i天当中都不改变,那么就是ww[1][i]*i(其中ww[][]数组是指第i-j天的最短路的值)
  • 如果需要在1~i-1天内进行改变,那么就会有改变前的价值+改变后(i-j)天的所需要的新的最短路总和+k(就是第二条方程)
  • 那接下来只要打把最短路(不要floyd)再来把dp就解决了。代码复杂程度较低。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <queue>
#define INF 0x7f7f7f
#define LL long long
using namespace std;
void fff(){
    freopen("trans.in","r",stdin);
    freopen("trans.out","w",stdout);
}
const int MAXN=500100;
struct Edge{
    int from,to,dist;
};
vector <Edge>edge;
vector <int> G[25];
bool stp[25][110];
bool visited[25],ex[25];
int dist[25];
int n,k,m,e,d;
queue <int> q;
int spfa(int a,int b){
    memset(visited,false,sizeof(visited));
    memset(dist,INF,sizeof(dist));
    memset(ex,false,sizeof(ex));
    while (!q.empty())q.pop();
    for (int i=1;i<=m;i++){
        for (int j=a;j<=b;j++){
            if(stp[i][j]) ex[i]=true;
        }
    }
    q.push(1);
    visited[1]=true;
    dist[1]=0;
    while (!q.empty()){
        int u=q.front();q.pop();
        int siz=G[u].size();
        for (int i=0;i<siz;i++){
            Edge &e=edge[G[u][i]];
            if(ex[e.to]) continue;
            if(dist[u]+e.dist<dist[e.to]){
                dist[e.to]=dist[u]+e.dist;
                if(!visited[e.to]){
                    visited[e.to]=true;
                    q.push(e.to);
                }
            }

        }
        visited[u]=false;
    }
    return dist[m];
}
int ww[111][111];
LL dp[111];
int main(){
//  fff();
    scanf("%d%d%d%d",&n,&m,&k,&e);
    for (int i=1;i<=e;i++){
        int f,t,s;
        scanf("%d%d%d",&f,&t,&s);
        edge.push_back((Edge){f,t,s});
        G[f].push_back(edge.size()-1);
        edge.push_back((Edge){t,f,s});
        G[t].push_back(edge.size()-1);
    }
    scanf("%d",&d);
    memset(stp,0,sizeof(stp));
    for (int i=1;i<=d;i++){
        int a,b,p;
        scanf("%d%d%d",&p,&a,&b);
        for (int j=a;j<=b;j++){
            stp[p][j]=true;
        }
    }
    for (int i=1;i<=n;i++){
        for (int j=1;j<=n;j++){
            ww[i][j]=spfa(i,j);
        }
    }
    for (int i=1;i<=n;i++){
        dp[i]=(LL)ww[1][i]*i;//
        for (int j=1;j<i;j++){
            dp[i]=min(dp[i],dp[j]+k+(LL)ww[j+1][i]*(i-j));
        }
    }
    cout<<dp[n];
    return 0;
}

猜你喜欢

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