acwing 刷题

13 篇文章 0 订阅
文章包含多个编程挑战,涉及寻找虫洞路径以回到过去、构建单词环以最大化平均长度、分配糖果以满足条件、安排排队布局以符合距离限制等,主要考察图论和算法应用,如SPFA和二分搜索等方法。
摘要由CSDN通过智能技术生成

目录

1 904 虫洞

2 1165 单词环

 3 1169 糖果

 4 1170 排队布局

5 1250 格子游戏

6 1252 搭配购买

7 1273 天才的记忆


1 904 虫洞

农夫约翰在巡视他的众多农场时,发现了很多令人惊叹的虫洞。

虫洞非常奇特,它可以看作是一条 单向 路径,通过它可以使你回到过去的某个时刻(相对于你进入虫洞之前)。

农夫约翰的每个农场中包含 N 片田地,M 条路径(双向)以及 W 个虫洞。

现在农夫约翰希望能够从农场中的某片田地出发,经过一些路径和虫洞回到过去,并在他的出发时刻之前赶到他的出发地。

他希望能够看到出发之前的自己。

请你判断一下约翰能否做到这一点。

下面我们将给你提供约翰拥有的农场数量 F,以及每个农场的完整信息。

已知走过任何一条路径所花费的时间都不超过 10000 秒,任何虫洞将他带回的时间都不会超过 10000秒。

输入格式

第一行包含整数 F,表示约翰共有 F 个农场。

对于每个农场,第一行包含三个整数 N,M,W

接下来 M 行,每行包含三个整数 S,E,T,表示田地 S 和 E 之间存在一条路径,经过这条路径所花的时间为 T。

再接下来 W 行,每行包含三个整数 S,E,T,表示存在一条从田地 S 走到田地 E 的虫洞,走过这条虫洞,可以回到 T 秒之前。

输出格式

输出共 F 行,每行输出一个结果。

如果约翰能够在出发时刻之前回到出发地,则输出 YES,否则输出 NO

数据范围

1≤F≤5
1≤N≤500
1≤M≤2500
1≤W≤200
1≤T≤10000
1≤S,E≤N

输入样例:

2
3 3 1
1 2 2
1 3 4
2 3 1
3 1 3
3 2 1
1 2 3
2 3 4
3 1 8

输出样例:

NO
YES

#include<bits/stdc++.h>
using namespace std;
const int N=5210;
int e[N],h[N],ne[N],w[N],idx;
int d[N],cnt[N];
bool st[N];
int n,m,k;
void add(int a,int b,int c)
{
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
bool spfa()
{
    queue<int>q;
    for(int i=1;i<=n;i++){
        q.push(i);
        st[i]=true;
    }
    memset(d,0,sizeof d);
    memset(cnt,0,sizeof cnt);
    while(q.size())
    {
        int t=q.front();
        q.pop();
        st[t]=false;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(d[j]>d[t]+w[i])
            {
                d[j]=d[t]+w[i];
                cnt[j]=cnt[t]+1;
                if(cnt[j]>=n)return true;
                if(!st[j])
                {
                    q.push(j);
                    st[j]=true;
                }
            }
        }
    }
    return false;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n>>m>>k;
        memset(h,-1,sizeof h);
        idx=0;
        while(m--){
            int a,b,c;
            cin>>a>>b>>c;
            add(a,b,c),add(b,a,c);
        }
        while(k--)
        {
            int a,b,c;
            cin>>a>>b>>c;
            add(a,b,-c);
        }
        if(spfa())puts("YES");
        else puts("NO");
    }
    return 0;
}

2 1165 单词环

我们有 n 个字符串,每个字符串都是由 a∼z 的小写英文字母组成的。

如果字符串 A 的结尾两个字符刚好与字符串 B 的开头两个字符相匹配,那么我们称 A 与 B 能够相连(注意:A 能与 B 相连不代表 B 能与 A 相连)。

我们希望从给定的字符串中找出一些,使得它们首尾相连形成一个环串(一个串首尾相连也算),我们想要使这个环串的平均长度最大。

如下例:

ababc
bckjaca
caahoynaab

第一个串能与第二个串相连,第二个串能与第三个串相连,第三个串能与第一个串相连,我们按照此顺序相连,便形成了一个环串,长度为 5+7+10=22(重复部分算两次),总共使用了 3 个串,所以平均长度是 22/3≈7.33

输入格式

本题有多组数据。

每组数据的第一行,一个整数 n,表示字符串数量;

接下来 n 行,每行一个长度小于等于 1000 的字符串。

读入以 n=0 结束。

输出格式

若不存在环串,输出”No solution”,否则输出最长的环串的平均长度。

只要答案与标准答案的差不超过 0.01,就视为答案正确。

数据范围

1≤n≤105

输入样例:

3
intercommunicational
alkylbenzenesulfonate
tetraiodophenolphthalein
0

输出样例:

21.66

#include<bits/stdc++.h>
using namespace std;
const int N=700,M=100010;
int e[M],ne[M],h[N],w[M];
int idx,cnt[N];
double d[N];
bool st[N];
int n;
void add(int a,int b,int c)
{
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
bool check(double mid)
{
    memset(st,0,sizeof st);
    memset(cnt,0,sizeof cnt);
    queue<int>q;
    for(int i=0;i<676;i++){
        q.push(i);
        st[i]=true;
    }
    int count=0;
    while(q.size())
    {
        int t=q.front();
        q.pop();
        st[t]=false;
        for(int i=h[t];i!=-1;i=ne[i]){
            int j=e[i];
            if(d[j]<d[t]+w[i]-mid){
                cnt[j]=cnt[t]+1;
                d[j]=d[t]+w[i]-mid;
                if(++count>10000)return true;
                if(cnt[j]>=N)return true;
                if(!st[j]){
                    q.push(j);
                    st[j]=true;
                }
            }
        }
    }
    return false;
}


int main()
{
    char s[1010];
    while(cin>>n,n)
    {
        memset(h,-1,sizeof h);
        idx=0;
        for(int i=0;i<n;i++){
            cin>>s;
            int len=strlen(s);
            if(len>=2)
            {
                int l=(s[0]-'a')*26+s[1]-'a';
                int r=(s[len-2]-'a')*26+s[len-1]-'a';
                add(l,r,len);
            }
        }
        if(!check(0))puts("No solution");
        else{
            double l=0,r=1000;
            while(r-l>1e-4){
                double mid=(l+r)/2;
                if(check(mid))l=mid;
                else r=mid;
            }
            printf("%lf\n",r);
        }
    }
    return 0;
}

 3 1169 糖果

幼儿园里有 N 个小朋友,老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。

但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候, 老师需要满足小朋友们的 K 个要求。

幼儿园的糖果总是有限的,老师想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。

输入格式

输入的第一行是两个整数 N,K。

接下来 K 行,表示分配糖果时需要满足的关系,每行 3 个数字 X,A,B。

  • 如果 X=1.表示第 A 个小朋友分到的糖果必须和第 B 个小朋友分到的糖果一样多。
  • 如果 X=2,表示第 A 个小朋友分到的糖果必须少于第 B 个小朋友分到的糖果。
  • 如果 X=3,表示第 A 个小朋友分到的糖果必须不少于第 B 个小朋友分到的糖果。
  • 如果 X=4,表示第 A 个小朋友分到的糖果必须多于第 B 个小朋友分到的糖果。
  • 如果 X=5,表示第 A 个小朋友分到的糖果必须不多于第 B 个小朋友分到的糖果。

小朋友编号从 1 到 N。

输出格式

输出一行,表示老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出 −1。

数据范围

1≤N<105
1≤K≤105
1≤X≤5
1≤A,B≤N
输入数据完全随机。

输入样例:

5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1

输出样例:

11
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100010,M=300010;
int n,m;
int h[N],e[M],ne[M],w[M],idx;
LL d[N];
int cnt[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
bool spfa()
{
    stack<int>q;
    memset(d,-0x3f,sizeof d);
    q.push(0);
    d[0]=0;
    st[0]=true;
    while(q.size())
    {
        int t=q.top();
        q.pop();
        st[t]=false;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(d[j]<d[t]+w[i])
            {
                d[j]=d[t]+w[i];
                cnt[j]=cnt[t]+1;
                if(cnt[j]>=n+1)return false;
                if(!st[j])
                {
                    q.push(j);
                    st[j]=true;
                }
            }
        }
    }
    return true;
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    while(m--)
    {
        int x,a,b;
        cin>>x>>a>>b;
        if(x==1)add(b,a,0),add(a,b,0);
        else if(x==2)add(a,b,1);
        else if(x==3)add(b,a,0);
        else if(x==4)add(b,a,1);
        else add(a,b,0);
    }
    for(int i=1;i<=n;i++)add(0,i,1);
    if(!spfa())puts("-1");
    else{
        LL ans=0;
        for(int i=1;i<=n;i++)ans+=d[i];
        cout<<ans<<endl;
    }
    return 0;
}

 4 1170 排队布局

当排队等候喂食时,奶牛喜欢和它们的朋友站得靠近些。

农夫约翰有 N 头奶牛,编号从 1 到 N,沿一条直线站着等候喂食。

奶牛排在队伍中的顺序和它们的编号是相同的。

因为奶牛相当苗条,所以可能有两头或者更多奶牛站在同一位置上。

如果我们想象奶牛是站在一条数轴上的话,允许有两头或更多奶牛拥有相同的横坐标。

一些奶牛相互间存有好感,它们希望两者之间的距离不超过一个给定的数 L。

另一方面,一些奶牛相互间非常反感,它们希望两者间的距离不小于一个给定的数 D。

给出 ML 条关于两头奶牛间有好感的描述,再给出 MD 条关于两头奶牛间存有反感的描述。

你的工作是:如果不存在满足要求的方案,输出-1;如果 1 号奶牛和 N 号奶牛间的距离可以任意大,输出-2;否则,计算出在满足所有要求的情况下,1 号奶牛和 N 号奶牛间可能的最大距离。

输入格式

第一行包含三个整数 N,ML,MD。

接下来 ML 行,每行包含三个正整数 A,B,L,表示奶牛 A 和奶牛 B 至多相隔 L 的距离。

再接下来 MD行,每行包含三个正整数 A,B,D,表示奶牛 A 和奶牛 B 至少相隔 D 的距离。

输出格式

输出一个整数,如果不存在满足要求的方案,输出-1;如果 1 号奶牛和 N 号奶牛间的距离可以任意大,输出-2;否则,输出在满足所有要求的情况下,1 号奶牛和 N 号奶牛间可能的最大距离。

数据范围

2≤N≤1000
1≤ML,MD≤104
1≤L,D≤106

输入样例:

4 2 1
1 3 10
2 4 20
2 3 3

输出样例:

27

#include<bits/stdc++.h>
using namespace std;
const int N=1010,M=21010,INF=0x3f3f3f3f;
int n,m1,m2;
int h[N],e[M],ne[M],w[M],idx;
int d[N],cnt[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
bool spfa(int u)
{
    queue<int>q;
    memset(st,0,sizeof st);
    memset(cnt,0,sizeof st);
    memset(d,0x3f,sizeof d);
    for(int i=1;i<=u;i++){
        d[i]=0;
        q.push(i);
        st[i]=true;
    }
    while(q.size())
    {
        int t=q.front();
        q.pop();
        st[t]=false;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(d[j]>d[t]+w[i]){
                d[j]=d[t]+w[i];
                cnt[j]=cnt[t]+1;
                if(cnt[j]>=n)return false;
                if(!st[j]){
                    q.push(j);
                    st[j]=true;
                }
            }
        }
    }
    return true;
}
int main()
{
    cin>>n>>m1>>m2;
    memset(h,-1,sizeof h);
    for(int i=1;i<n;i++)add(i+1,i,0);
    while(m1--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        if(b<a)swap(b,a);
        add(a,b,c);
    }
    while(m2--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        if(b<a)swap(a,b);
        add(b,a,-c);
    }
    if(!spfa(n))puts("-1");
    else{
        spfa(1);
        if(d[n]==INF)puts("-2");
        else cout<<d[n]<<endl;
    }
    return 0;
}

5 1250 格子游戏

Alice和Bob玩了一个古老的游戏:首先画一个 n×n的点阵(下图 n=3 )。

接着,他们两个轮流在相邻的点之间画上红边和蓝边:

直到围成一个封闭的圈(面积不必为 1)为止,“封圈”的那个人就是赢家。因为棋盘实在是太大了,他们的游戏实在是太长了!

他们甚至在游戏中都不知道谁赢得了游戏。

于是请你写一个程序,帮助他们计算他们是否结束了游戏?

输入格式

输入数据第一行为两个整数 n 和 m。n表示点阵的大小,m 表示一共画了 m 条线。

以后 m 行,每行首先有两个数字 (x,y),代表了画线的起点坐标,接着用空格隔开一个字符,假如字符是 D,则是向下连一条边,如果是 R 就是向右连一条边。

输入数据不会有重复的边且保证正确。

输出格式

输出一行:在第几步的时候结束。

假如 m 步之后也没有结束,则输出一行“draw”。

数据范围

1≤n≤200,
1≤m≤24000

输入样例:

3 5
1 1 D
1 1 R
1 2 D
2 1 R
2 2 D

输出样例:

4

#include<bits/stdc++.h>
using namespace std;
int p[40010];
int n,m;
int get(int x,int y)
{
    return x*n+y;
}
int find(int x)
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}
int main()
{
    int ans=0;
    cin>>n>>m;
    for(int i=0;i<n*n;i++)p[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x,y,b,a;
        char s;
        cin>>x>>y>>s;
        x--,y--;
        a=get(x,y);
        if(s=='D')b=get(x+1,y);
        else b=get(x,y+1);
        int pa=find(a),pb=find(b);
        if(pa==pb){
            ans=i;
            break;
        }
        p[pa]=pb;
    }
    if(!ans)puts("draw");
    else cout<<ans<<endl;
    return 0;
}

6 1252 搭配购买

Joe觉得云朵很美,决定去山上的商店买一些云朵。

商店里有 n 朵云,云朵被编号为 1,2,…,n,并且每朵云都有一个价值。

但是商店老板跟他说,一些云朵要搭配来买才好,所以买一朵云则与这朵云有搭配的云都要买。

但是Joe的钱有限,所以他希望买的价值越多越好。

输入格式

第 11 行包含三个整数 n,m,w,表示有 n 朵云,m 个搭配,Joe有 w 的钱。

第 2∼n+1行,每行两个整数 ci,di 表示 i 朵云的价钱和价值。

第 n+2∼n+1+m 行,每行两个整数 ui,vi,表示买 ui 就必须买 vi,同理,如果买 vi 就必须买 ui。

输出格式

一行,表示可以获得的最大价值。

数据范围

1≤n≤10000
0≤m≤5000
1≤w≤10000
1≤ci≤5000
1≤di≤100
1≤ui,vi≤n

输入样例:

5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2

输出样例:

1
#include<bits/stdc++.h>
using namespace std;
int w[10010],v[10010],f[10010],p[10010];
int n,m,V;
int find(int x)
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}
int main()
{
    cin>>n>>m>>V;
    for(int i=1;i<=n;i++)p[i]=i;
    for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        a=find(a),b=find(b);
        if(a!=b){
            v[b]+=v[a];
            w[b]+=w[a];
            p[a]=b;
        }
    }
    for(int i=1;i<=n;i++){
        if(p[i]==i){
            for(int j=V;j>=v[i];j--){
                f[j]=max(f[j],f[j-v[i]]+w[i]);
            }
        }
    }
    cout<<f[V];
    return 0;
}

7 1273 天才的记忆

从前有个人名叫 WNB,他有着天才般的记忆力,他珍藏了许多许多的宝藏。

在他离世之后留给后人一个难题(专门考验记忆力的啊!),如果谁能轻松回答出这个问题,便可以继承他的宝藏。

题目是这样的:给你一大串数字(编号为 1 到 N,大小可不一定哦!),在你看过一遍之后,它便消失在你面前,随后问题就出现了,给你 M 个询问,每次询问就给你两个数字 A,B,要求你瞬间就说出属于 A 到 B 这段区间内的最大数。

一天,一位美丽的姐姐从天上飞过,看到这个问题,感到很有意思(主要是据说那个宝藏里面藏着一种美容水,喝了可以让这美丽的姐姐更加迷人),于是她就竭尽全力想解决这个问题。

但是,她每次都以失败告终,因为这数字的个数是在太多了!

于是她请天才的你帮他解决。如果你帮她解决了这个问题,可是会得到很多甜头的哦!

输入格式

第一行一个整数 N 表示数字的个数。

接下来一行为 N 个数,表示数字序列。

第三行读入一个 M,表示你看完那串数后需要被提问的次数。

接下来 M 行,每行都有两个整数 A,B。

输出格式

输出共 M 行,每行输出一个数,表示对一个问题的回答。

数据范围

1≤N≤2×105
1≤M≤104
1≤A≤B≤N

输入样例:

6
34 1 8 123 3 2
4
1 2
1 5
3 4
2 3

输出样例:

34
123
123
8

 

#include<bits/stdc++.h>
using namespace std;
const int N=200010,M=18;
int n,m;
int w[N],f[N][M];
void init()
{
    for(int j=0;j<M;j++){
        for(int i=1;i+(1<<j)-1<=n;i++){
            if(!j)f[i][j]=w[i];
            else f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);
        }
    }
}
int query(int l,int r)
{
    int len=r-l+1;
    int k=log(len)/log(2);
    return max(f[l][k],f[r-(1<<k)+1][k]);
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)cin>>w[i];
    cin>>m;
    init();
    while(m--)
    {
        int l,r;
        cin>>l>>r;
        cout<<query(l,r)<<endl;
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值