码迷,mamicode.com
首页 > 其他好文 > 详细

algorithm_group_Round3题解

时间:2020-04-17 23:23:03      阅读:66      评论:0      收藏:0      [点我收藏+]

标签:最长路   之间   区间更新   魔法   open   sed   ini   iostream   传递   

A.小希的迷宫(并查集)

题意:

上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走。但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道连通了房间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。

题解:

即判断一个图是否成环,用并查集实现,当前要合并的两个节点属于一个集合时,输出NO

技术图片
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<unordered_map>
using namespace std;
const int maxn=1e6+14;
const int inf=1e9;
int father[maxn],isRoot[maxn],flag,visit[maxn];
void init () {
    for (int i=0;i<maxn;i++) father[i]=i;
    flag=0;
    fill (isRoot,isRoot+maxn,0);
    fill (visit,visit+maxn,0);
}
int findfather (int x) {
    int a=x;
    while (x!=father[x]) x=father[x];
    while (a!=father[a]) {
        int z=a;
        a=father[a];
        father[z]=x;
    }
    return x;
}
void Union (int a,int b) {
    int faA=findfather(a);
    int faB=findfather(b);
    if (faA!=faB) father[faA]=faB;
}
int main () {
    while (1) {
        int flag=0,x,y;
        init ();
        while (1) {
            scanf ("%d %d",&x,&y);
            if (x==0&&y==0) break; 
            if (x==-1&&y==-1) return 0;
            if (findfather(x)==findfather(y)) flag++;
            visit[x]=1;visit[y]=1;
            Union (x,y);
        }
        for (int i=0;i<maxn;i++) if (visit[i]) isRoot[findfather(i)]++;
        int ans=0;
        for (int i=0;i<maxn;i++) if (isRoot[i]&&visit[i]) ans++;
        if (ans>1) flag++; 
        //printf ("%d\n",ans);
        if (flag==0) printf ("Yes\n");
        else printf ("No\n");
    }
    return 0;
}
View Code

 

B.Eugene and an array(前缀和)

题意:

一个区间中若有一段连续的子区间的区间和为0,则这个区间是 不好的 ,给定一个区间求这个区间内有多少 好 的子区间

题解:

从左往右遍历,如果当前的前缀和在map里出现过,说明这个前缀和之前的位置到当前位置的序列是不合法的,统计到之前位置的序列就好。时间复杂度O(N)

技术图片
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
int a[maxn];
map<ll,ll> pos;
int main () {
    int N;
    scanf("%d",&N);
    pos[0]=1;
    ll sum=0,ans=0,Max=1;
    for (int i=2;i<=N+1;i++) {
        scanf("%d",&a[i]);
        sum+=a[i];
        if (pos[sum]!=0) 
            Max=max(Max,pos[sum]+1);
        ans+=i-Max;
        pos[sum]=i;
    }
    printf("%lld\n",ans);
}
View Code

 

C.Circles of Monsters(前缀和)

题意:

你在玩另一个电脑游戏,现在你必须杀死n个怪物。这些怪物站成一个圈,顺时针从1到n编号。最初,第i个怪物有ai生命值。

你可以射击怪物杀死他们。每次射击只需要一颗子弹,并且使目标怪物的生命值降低1(对其造成1点伤害)。此外,当某个怪物i的生命值变为0或小于0时,它会死亡并爆炸,对下一个怪物造成bi伤害(如果i如果下一个怪物已经死了,那么什么也不会发生。如果爆炸杀死了下一个怪物,它也会爆炸,在爆炸后破坏怪物并可能引发另一个爆炸,等等。

你必须计算你必须发射的子弹的最小数量,以杀死所有的n个怪物在圆。

题解:

给出一圈怪兽,每个怪兽都有生命值和攻击力,当你击杀一个怪兽,这个怪兽会对下一个怪兽造成等于它攻击力的伤害,依次传递,当无法击杀怪兽或者碰到已经死亡的怪兽就停止。询问至少要消灭多少生命值的怪兽才能击杀所有怪兽。

做法是开一个两倍的数组模拟循环,然后开一个C数组,表示以每个怪兽为起点发动攻击,所消耗的子弹数量,可以推导状态转移方程:

if (a[i]<=b[i-1])
    c[i]=c[i-1];
else 
    c[i]=c[i-1]+a[i]-b[i-1];
if (i>=N)
    ans=min(ans,c[i]-c[i-N+1]+a[i-N+1]);

完整代码:

技术图片
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
int a[maxn];
map<ll,ll> pos;
int main () {
    int N;
    scanf("%d",&N);
    pos[0]=1;
    ll sum=0,ans=0,Max=1;
    for (int i=2;i<=N+1;i++) {
        scanf("%d",&a[i]);
        sum+=a[i];
        if (pos[sum]!=0) 
            Max=max(Max,pos[sum]+1);
        ans+=i-Max;
        pos[sum]=i;
    }
    printf("%lld\n",ans);
}
View Code

 

D.National Road System(最小生成树)

题意:

给出n个城市的x,y坐标以及每个城市的人数, 这些城市的主人想建造最小生成树,这时候有个会魔法的道士说, 我可以让一条路权值为0, 求A/B的最大值, 其中A是权值为0的道路连接的城市的人数之和, B是最小生成树的权值。

题解:

再遍历每一对顶点,如果该顶点之间的边属于最小生成树,则剪掉这对顶点在最小生成树里的最长路径

否则直接剪掉连接这对顶点的边~

用prim算法求最小生成树最长路径的模板~

技术图片
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
const int maxn=1010;
const int inf=1e9;
double g[maxn][maxn],d[maxn],w[maxn];
int visit[maxn],N,S,P,pre[maxn];
double len[maxn][maxn];//表示点i,j在最小生成树上的最长直径
double c[maxn][maxn];
void init () {
    for (int i=0;i<maxn;i++)
        for (int j=0;j<maxn;j++) 
            g[i][j]=inf,c[i][j]=0,len[i][j]=0;
} 
double prim (int s) {
    fill(d,d+maxn,inf);
    fill(visit,visit+maxn,0);
    for (int i=1;i<=N;i++) pre[i]=i;
    d[s]=0;
    double 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;
        visit[u]=1;
        ans+=d[u];
        if (pre[u]!=u) 
            c[u][pre[u]]=c[pre[u]][u]=1;
        for (int v=1;v<=N;v++) 
            if (!visit[v]&&g[u][v]!=inf&&g[u][v]<d[v]) {
                d[v]=g[u][v];
                pre[v]=u;
            }
        for (int v=1;v<=N;v++) 
            if (visit[v]&&u!=v)
                len[u][v]=len[v][u]=max(len[pre[u]][v],d[u]);
    }
    return ans;
}
struct node {
    double x,y;
}Node[maxn];
int main () {
    int T;
    scanf("%d",&T);
    while (T--) {
        scanf("%d",&N);
        for (int i=1;i<=N;i++) 
            scanf("%lf%lf%lf",&Node[i].x,&Node[i].y,&w[i]);
            init();
            for (int i=1;i<=N;i++) 
                for (int j=i+1;j<=N;j++) 
                    g[i][j]=g[j][i]=sqrt((Node[i].x-Node[j].x)*(Node[i].x-Node[j].x)+
                    (Node[i].y-Node[j].y)*(Node[i].y-Node[j].y));
            double mst=prim(1);
            double Max=0;
            for (int i=1;i<=N;i++)
                for (int j=i+1;j<=N;j++)
                    if (!c[i][j]) 
                        Max=max(Max,(w[i]+w[j])/(mst-len[i][j]));
                    else 
                        Max=max(Max,(w[i]+w[j])/(mst-g[i][j]));
                    printf("%.2f\n",Max);            
    }
}
View Code


E.Just a Hook(线段树)

题意:

初始的时候,整个序列都是1,接下来,每次输入l,r,x。表示将l到r之间修改为x且x只会是1、2、3,最后问你序列总和。

题解:

线段树+延迟标记,老模板题了

技术图片
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e6+100;
struct node {
    int l,r,sum,lazy;
}segTree[maxn*4];
int N,M;
void build (int i,int l,int r) {
    segTree[i].l=l;
    segTree[i].r=r;
    segTree[i].lazy=0;
    if (l==r) {
        segTree[i].sum=1;
        return;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    segTree[i].sum=segTree[i<<1].sum+segTree[i<<1|1].sum;
}
void spread (int i) {
    if (segTree[i].lazy) {
        segTree[i<<1].sum=segTree[i].lazy*(segTree[i<<1].r-segTree[i<<1].l+1);
        segTree[i<<1|1].sum=segTree[i].lazy*(segTree[i<<1|1].r-segTree[i<<1|1].l+1);
        segTree[i<<1].lazy=segTree[i].lazy;
        segTree[i<<1|1].lazy=segTree[i].lazy;
        segTree[i].lazy=0;
    }
} 
void update (int i,int l,int r,int val) {
    //区间更新
    if (l<=segTree[i].l&&segTree[i].r<=r) {
        segTree[i].sum=val*(segTree[i].r-segTree[i].l+1);
        segTree[i].lazy=val;
        return;
    } 
    spread(i);
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if (l<=mid) 
        update(i<<1,l,r,val);
    if (r>mid)
        update(i<<1|1,l,r,val);
    segTree[i].sum=segTree[i<<1].sum+segTree[i<<1|1].sum;
}
int query (int i,int l,int r) {
    if (l<=segTree[i].l&&r>=segTree[i].r)
        return segTree[i].sum;
    spread(i);
    int mid=(segTree[i].l+segTree[i].r)>>1;
    int ans=0;
    if (l<=mid) 
        ans+=query(i<<1,l,r);
    if (r>mid)
        ans+=query(i<<1|1,l,r);
    return ans; 
}
int T;
int main () {
    scanf("%d",&T);
    for (int k=1;k<=T;k++) {
        scanf("%d%d",&N,&M);
        build(1,1,N);
        for (int i=1;i<=M;i++) {
            int l,r,x;
            scanf("%d%d%d",&l,&r,&x);
            update(1,l,r,x);
        }
        printf("Case %d: The total value of the hook is %d.\n",k,query(1,1,N));
    }
}
View Code

 

F.I Hate It(线段树)

题意:

很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。
这让很多学生很反感。

不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。

题解:

线段树单点修改+区间查询,属于简单题

技术图片
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
using namespace std;
const int maxn=2e5+100;
struct node {
    int l,r,sum,lazy;
}segTree[maxn*4];
int a[maxn];
int N,M;
void build (int i,int l,int r) {
    segTree[i].l=l;
    segTree[i].r=r;
    segTree[i].lazy=0;
    if (l==r) {
        segTree[i].sum=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    segTree[i].sum=max(segTree[i<<1].sum,segTree[i<<1|1].sum);
}
void update (int i,int x,int y) {
    if (segTree[i].l==x&&segTree[i].r==x) {
        segTree[i].sum=y;
        return;
    }
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if (x<=mid) 
        update(i<<1,x,y);
    else 
        update(i<<1|1,x,y);
    segTree[i].sum=max(segTree[i<<1].sum,segTree[i<<1|1].sum);
}
int query (int i,int l,int r) {
    if (segTree[i].l>=l&&segTree[i].r<=r) {
        return segTree[i].sum;
    } 
    int mid=(segTree[i].l+segTree[i].r)>>1;
    int Max=-1;
    if (l<=mid)
        Max=max(Max,query(i<<1,l,r));
    if (r>mid)
        Max=max(Max,query(i<<1|1,l,r));
    return Max;
}
int main () {
    while (~scanf("%d%d",&N,&M)) {
        for (int i=1;i<=N;i++) scanf("%d",&a[i]);
        build(1,1,N);
        string s;
        int x,y;
        for (int i=1;i<=M;i++) {
            cin>>s>>x>>y;
            if (s=="Q") {
                printf("%d\n",query(1,x,y));
            }
            else {
                update(1,x,y);
            }
        }
    }
}
View Code

 

 G.BillBoard(线段树)

题意:

题解:

把整个图竖过来再用线段树做,有点深奥,我也没有完全掌握,看的别人的题解..
 
技术图片
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e6+15;
struct node {
    int l,r,sum;
}segTree[maxn*4];
int h,w,n;
void build (int i,int l,int r) {
    segTree[i].l=l;
    segTree[i].r=r;
    segTree[i].sum=w;
    if (l==r) return;
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    segTree[i].sum=max(segTree[i<<1].sum,segTree[i<<1|1].sum);
}
int query (int i,int x) {
    if (segTree[i].l==segTree[i].r) {
        segTree[i].sum-=x;
        return segTree[i].l;
    }
    int mid=(segTree[i].l+segTree[i].r)>>1;
    int ans;
    if (segTree[i<<1].sum>=x)
        ans=query(i<<1,x);
    else 
        ans=query(i<<1|1,x);
    segTree[i].sum=max(segTree[i<<1].sum,segTree[i<<1|1].sum);
    return ans;
}
int main () {
    while (~scanf("%d%d%d",&h,&w,&n)) {
        h=min(h,n);
        build(1,1,h);
        int x;
        while (n--) {
            scanf("%d",&x);
            if (segTree[1].sum<x) 
                printf("-1\n");
            else 
                printf("%d\n",query(1,x));
        }
    }
}
View Code

 

algorithm_group_Round3题解

标签:最长路   之间   区间更新   魔法   open   sed   ini   iostream   传递   

原文地址:https://www.cnblogs.com/algorithmgroup/p/12723165.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!