algorithm_group_Round4题解

A.Highway Project(最短路径+最小生成树)

题意:

给出一张有向图,询问起点到剩下所有点的最短路径长度之和,并在此基础上计算修建公路的最小费用(即所有路径的边权总和,重复的边权不算)

题解:

双条件dijkstra算法求解,在更新费用的时候用prim算法的更新方式。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+200;
const ll inf=1e17;
struct node {
    int u,v,next;
    ll w,c;
}edge[maxn*4];
int N,M;
int head[maxn];
int tol=0;
void addedge (int u,int v,ll w,ll c) {
    edge[tol].u=u;
    edge[tol].v=v;
    edge[tol].w=w;
    edge[tol].c=c;
    edge[tol].next=head[u];
    head[u]=tol++;
}
ll d[maxn];
ll c[maxn];
int visit[maxn];
struct qnode {
    int v;
    ll w,c;
    bool operator < (const qnode &r) const {
        if (w!=r.w) 
            return w>r.w;
        else 
            return c>r.c;
    }
};
void dijkstra (int s) {
    memset(visit,0,sizeof(visit));
    for (int i=0;i<=N;i++) d[i]=inf,c[i]=inf;
    priority_queue<qnode> q;
    d[s]=0;
    c[s]=0;
    q.push({s,0,0});
    qnode tmp;
    while (!q.empty()) {
        tmp=q.top();
        q.pop();
        int u=tmp.v;
        if (visit[u]) continue;
        visit[u]=1;
        for (int i=head[u];i!=-1;i=edge[i].next) {
            int v=edge[i].v;
            if (d[v]>d[u]+edge[i].w) {
                d[v]=d[u]+edge[i].w;
                c[v]=edge[i].c;
                q.push({v,d[v],c[v]});
            }
            else if (d[v]==d[u]+edge[i].w) {
                if (c[v]>edge[i].c) {
                    c[v]=edge[i].c;
                    q.push({v,d[v],c[v]});
                }
            }
        }
    }
}
int main () {
    int T;
    scanf("%d",&T);
    while (T--) {
        memset(head,-1,sizeof(head));
        tol=0;
        scanf("%d%d",&N,&M);
        for (int i=1;i<=M;i++) {
            int u,v;
            ll w,c;
            scanf("%d%d%lld%lld",&u,&v,&w,&c);
            addedge(u,v,w,c);
            addedge(v,u,w,c);
        }
        dijkstra(0);
        ll ans=0,cost=0;
        for (int i=1;i<N;i++) ans+=d[i],cost+=c[i];
        printf("%lld %lld\n",ans,cost);
    }
}
View Code

B.Distince Character Queries(树状数组)

题意:

你有一个字符串s组成的小写拉丁字母和q查询这个字符串。

回想一下,字符串s的子字符串s[l;r]是字符串slsl+1…sr。例如,“codeforce”的子字符串是“code”、“force”、“f”、“For”,而不是“coder”和“top”。

有两种查询类型:

1 pos c(1≤pos≤|s|, c为小写拉丁字母):将spos替换为c (set spos:=c);

2 l r(1≤l≤r≤|s|):计算子串s[l;r]中不同字符的个数。

题解:

开26个树状数组,存储26个字母的前缀和。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
string s;
int c[26][maxn];
int len;
int lowbit (int x) {
    return x&-x;
} 
void update (int id,int x,int val) {
    for (int i=x;i<=len;i+=lowbit(i)) c[id][i]+=val;
}
int getsum (int id,int x) {
    int ans=0;
    for (int i=x;i;i-=lowbit(i)) ans+=c[id][i];
    return ans;
}
int main () {
    cin>>s;
    int N;
    scanf("%d",&N);
    len=s.length();
    for (int i=1;i<=len;i++) 
        update(s[i-1]-'a',i,1);
    for (int i=1;i<=N;i++) {
        int f;
        scanf("%d",&f);
        if (f==1) {
            int x;
            char ch;
            scanf("%d %c",&x,&ch);
            update(s[x-1]-'a',x,-1);
            update(ch-'a',x,1);
            s[x-1]=ch;
        }
        else {
            int l,r;
            scanf("%d%d",&l,&r);
            int ans=0;
            for (int j=0;j<26;j++) 
                if (getsum(j,r)-getsum(j,l-1)>=1)
                    ans++;
            printf("%d\n",ans);
        }
    }
}
View Code

C.Networking(最小生成树)

题意:

prim算法模板。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
const int maxn=1010;
const int inf=1e9;
int g[maxn][maxn];
int d[maxn];
int visit[maxn];
int N,M;
void init () {
    for (int i=0;i<maxn;i++)
        for (int j=0;j<maxn;j++)
            g[i][j]=inf;
}
int prim (int s) {
    fill(d,d+maxn,inf);
    fill(visit,visit+maxn,0);
    d[s]=0;
    int ans=0;
    for (int i=1;i<=N;i++) {
        int u=-1,min=inf;
        for (int j=1;j<=N;j++)
            if (!visit[j]&&d[j]<min) {
                u=j;
                min=d[j];
            }
        if (u==-1) return -1;
        ans+=d[u];
        visit[u]=true;
        for (int v=1;v<=N;v++)  {
            if (!visit[v]&&g[u][v]!=inf&&g[u][v]<d[v]) 
                d[v]=g[u][v];
        }
    }
    return ans;
}
int main () {
    while (scanf("%d%d",&N,&M)&&N) {
        init();
        int u,v,x;
        for (int i=0;i<M;i++) {
            scanf("%d%d%d",&u,&v,&x);
            g[u][v]=min(g[u][v],x);
            g[v][u]=g[u][v];
        }
        printf("%d\n",prim(1));
    }
}
View Code

D.Frogger(最短路径)

题意:

湖中有n块石头,编号从1到n,有两只青蛙,Bob在1号石头上,Alice在2号石头上,Bob想去看望Alice,但由于水很脏,他想避免游泳,于是跳着去找她。但是Alice的石头超出了他的跳跃范围。因此,Bob使用其他石头作为中间站,通过一系列的小跳跃到达她。两块石头之间的青蛙距离被定义为两块石头之间所有可能路径上的最小必要跳跃距离,某条路径的必要跳跃距离即这条路径中单次跳跃的最远跳跃距离。你的工作是计算Alice和Bob石头之间的青蛙距离。

题解:

用floyd算法求解最短路径中的最大边权。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1050;
const int inf=1e9;
struct node {
    int x,y;
}Node[maxn];
int N;
double g[maxn][maxn];
double getDis (int i,int j) {
    double x=Node[i].x-Node[j].x;
    double y=Node[i].y-Node[j].y;
    return sqrt(x*x+y*y);
}
void floyd () {
    for (int k=1;k<=N;k++)
        for (int i=1;i<=N;i++)
            for (int j=1;j<=N;j++)
                g[i][j]=min(g[i][j],max(g[i][k],g[k][j]));
}
int main () {
    int cnt=1;
    while (scanf("%d",&N)&&N) {
        for (int i=1;i<=N;i++)
            for (int j=1;j<=N;j++)
                g[i][j]=inf;
        for (int i=1;i<=N;i++) 
            scanf("%d%d",&Node[i].x,&Node[i].y);
        for (int i=1;i<=N;i++)
            for (int j=1;j<=N;j++)
                g[i][j]=g[j][i]=min(getDis(i,j),g[i][j]);
        floyd();
        printf("Scenario #%d\n",cnt++);
        printf("Frog Distance = %.3f\n\n",g[1][2]);
    }
}
View Code

EFG线段树模板,不表

猜你喜欢

转载自www.cnblogs.com/algorithmgroup/p/12730427.html