acwing 刷题

13 篇文章 0 订阅

目录

1 1141 局域网

2 1142 繁忙的都市

3 1143 联络员

 4 1144 连接格点

5 1145 北极通讯网络

6 1146 新的开始

 7 1148 秘密的牛奶运输


1 1141 局域网

某个局域网内有 n台计算机和 k 条 双向 网线,计算机的编号是 1∼n。由于搭建局域网时工作人员的疏忽,现在局域网内的连接形成了回路,我们知道如果局域网形成回路那么数据将不停的在回路内传输,造成网络卡的现象。

注意

  • 对于某一个连接,虽然它是双向的,但我们不将其当做回路。本题中所描述的回路至少要包含两条不同的连接。
  • 两台计算机之间最多只会存在一条连接。
  • 不存在一条连接,它所连接的两端是同一台计算机。

因为连接计算机的网线本身不同,所以有一些连线不是很畅通,我们用 f(i,j) 表示 i,j 之间连接的畅通程度,f(i,j) 值越小表示 i,j 之间连接越通畅。

现在我们需要解决回路问题,我们将除去一些连线,使得网络中没有回路且不影响连通性(即如果之前某两个点是连通的,去完之后也必须是连通的),并且被除去网线的 Σf(i,j) 最大,请求出这个最大值。

输入格式

第一行两个正整数 n,k

接下来的 k 行每行三个正整数 i,j,m 表示 i,j 两台计算机之间有网线联通,通畅程度为 m。

输出格式

一个正整数,表示被除去网线的 Σf(i,j) 的最大值。

数据范围

1≤n≤100
0≤k≤200
1≤f(i,j)≤1000

输入样例:

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

输出样例:

8

#include<bits/stdc++.h>
using namespace std;
int p[210];
struct T{
    int a,b,w;
}e[210];

int find(int x)
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}
bool cmp(T a,T b)
{
    return a.w<b.w;
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++)p[i]=i;
    for(int i=0;i<m;i++)
    {
        int a,b,w;
        cin>>a>>b>>w;
        e[i]={a,b,w};
    }
    sort(e,e+m,cmp);
    int ans=0;
    for(int i=0;i<m;i++){
        int a=find(e[i].a),b=find(e[i].b),w=e[i].w;
        if(a!=b)p[a]=b;
        else ans+=w;
    }
    cout<<ans;
    return 0;
}

2 1142 繁忙的都市

城市C是一个非常繁忙的大都市,城市中的道路十分的拥挤,于是市长决定对其中的道路进行改造。

城市C的道路是这样分布的:

城市中有 n 个交叉路口,编号是 1∼n,有些交叉路口之间有道路相连,两个交叉路口之间最多有一条道路相连接。

这些道路是 双向 的,且把所有的交叉路口直接或间接的连接起来了。

每条道路都有一个分值,分值越小表示这个道路越繁忙,越需要进行改造。

但是市政府的资金有限,市长希望进行改造的道路越少越好,于是他提出下面的要求:

1.改造的那些道路能够把所有的交叉路口直接或间接的连通起来。

2.在满足要求1的情况下,改造的道路尽量少。

3.在满足要求1、2的情况下,改造的那些道路中分值最大值尽量小。

作为市规划局的你,应当作出最佳的决策,选择哪些道路应当被修建。

输入格式

第一行有两个整数 n,m 表示城市有 n 个交叉路口,m 条道路。

接下来 m 行是对每条道路的描述,每行包含三个整数u,v,c 表示交叉路口 u和 v 之间有道路相连,分值为 c。

输出格式

两个整数 s,max,表示你选出了几条道路,分值最大的那条道路的分值是多少。

数据范围

1≤n≤300
1≤m≤8000
1≤c≤10000

输入样例:

4 5
1 2 3
1 4 5
2 4 7
2 3 6
3 4 8

输出样例:

3 6
#include<bits/stdc++.h>
using namespace std;
int p[310];
struct T{
    int a,b,w;
}s[10010];
bool cmp(T a,T b)
{
    return a.w<b.w;
}
int find (int x)
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)p[i]=i;
    for(int i=0;i<m;i++){
        int a,b,w;
        cin>>a>>b>>w;
        s[i]={a,b,w};
    }
    sort(s,s+m,cmp);
    int ans;
    for(int i=0;i<m;i++){
        int a=find(s[i].a),b=find(s[i].b),w=s[i].w;
        if(a!=b){
            p[a]=b;
            ans=w;
        }
    }
    cout<<n-1<<" "<<ans;
    return 0;
}

3 1143 联络员

Tyvj已经一岁了,网站也由最初的几个用户增加到了上万个用户,随着Tyvj网站的逐步壮大,管理员的数目也越来越多,现在你身为Tyvj管理层的联络员,希望你找到一些通信渠道,使得管理员两两都可以联络(直接或者是间接都可以)。本题中所涉及的通信渠道都是 双向 的。

Tyvj是一个公益性的网站,没有过多的利润,所以你要尽可能的使费用少才可以。

目前你已经知道,Tyvj的通信渠道分为两大类,一类是必选通信渠道,无论价格多少,你都需要把所有的都选择上;还有一类是选择性的通信渠道,你可以从中挑选一些作为最终管理员联络的通信渠道。

数据保证给出的通信渠道可以让所有的管理员联通。

注意: 对于某两个管理员 u,v,他们之间可能存在多条通信渠道,你的程序应该累加所有 u,v 之间的必选通行渠道。

输入格式

第一行两个整数 n,m 表示Tyvj一共有 n 个管理员,有 m 个通信渠道;

第二行到 m+1 行,每行四个非负整数,p,u,v,w 当 p=1 时,表示这个通信渠道为必选通信渠道;当 p=2 时,表示这个通信渠道为选择性通信渠道;u,v,w 表示本条信息描述的是 u,v 管理员之间的通信渠道,u 可以收到 v 的信息,v 也可以收到 u 的信息,w 表示费用。

输出格式

一个整数,表示最小的通信费用。

数据范围

1≤n≤2000
1≤m≤10000

输入样例:

5 6
1 1 2 1
1 2 3 1
1 3 4 1
1 4 1 1
2 2 5 10
2 2 5 5

输出样例:

9
#include<bits/stdc++.h>
using namespace std;
int p[2010];
struct T{
    int a,b,w;
}s[10010];
bool cmp(T a,T b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}
int main()
{
    int n,m;
    int ans=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++)p[i]=i;
    int k=0;
    while(m--){
        int t,a,b,c;
        cin>>t>>a>>b>>c;
        if(t==1){
            p[find(a)]=find(b);
            ans+=c;
        }else{
            s[k++]={a,b,c};
        }
    }
    sort(s,s+k,cmp);
    for(int i=0;i<k;i++){
        int a=find(s[i].a),b=find(s[i].b),w=s[i].w;
        if(a!=b){
            p[a]=b;
            ans+=w;
        }
    }
    cout<<ans;
    return 0;
}

 4 1144 连接格点

有一个 m 行 n 列的点阵,相邻两点可以相连。

一条纵向的连线花费一个单位,一条横向的连线花费两个单位。

某些点之间已经有连线了,试问至少还需要花费多少个单位才能使所有的点全部连通。

输入格式

第一行输入两个正整数 m 和 n。

以下若干行每行四个正整数 x1,y1,x2,y2,表示第 x1行第 y1 列的点和第 x2 行第 y2 列的点已经有连线。

输入保证|x1−x2|+|y1−y2|=1

输出格式

输出使得连通所有点还需要的最小花费。

数据范围

1≤m,n≤1000
0≤已经存在的连线数≤10000

输入样例:

2 2
1 1 2 1

输出样例:

3

#include<bits/stdc++.h>
using namespace std;
const int N=1010,M=N*N,K=2*M;
int n,m,k;
int ids[N][N];
struct T{
    int a,b,w;
}s[K];
int p[M];
int find (int x)
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}
void get()
{
    int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1},dw[4]={1,2,1,2};
    for(int z=0;z<2;z++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                for(int u=0;u<4;u++){
                    if(u%2==z){
                        int x=i+dx[u],y=j+dy[u],w=dw[u];
                        if(x&&x<=n&&y&&y<=m){
                            int a=ids[i][j],b=ids[x][y];
                            if(a<b)s[k++]={a,b,w};
                        }
                    }
                }
            }
        }
    }

}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n*m;i++)p[i]=i;
    for(int i=1,t=1;i<=n;i++){
        for(int j=1;j<=m;j++,t++){
            ids[i][j]=t;
        }
    }
    int x1,y1,x2,y2;
    while(cin>>x1>>y1>>x2>>y2){
        int a=ids[x1][y1],b=ids[x2][y2];
        p[find(a)]=find(b);
    }
    get();
    int ans=0;
    for(int i=0;i<k;i++){
        int a=find(s[i].a),b=find(s[i].b),w=s[i].w;
        if(a!=b){
            ans+=w;
            p[a]=b;
        }
    }
    cout<<ans;
    return 0;
}

5 1145 北极通讯网络

北极的某区域共有 n 座村庄,每座村庄的坐标用一对整数 (x,y) 表示。

为了加强联系,决定在村庄之间建立通讯网络,使每两座村庄之间都可以直接或间接通讯。

通讯工具可以是无线电收发机,也可以是卫星设备。

无线电收发机有多种不同型号,不同型号的无线电收发机有一个不同的参数 d,两座村庄之间的距离如果不超过 d,就可以用该型号的无线电收发机直接通讯,d 值越大的型号价格越贵。现在要先选择某一种型号的无线电收发机,然后统一给所有村庄配备,数量不限,但型号都是 相同的

配备卫星设备的两座村庄无论相距多远都可以直接通讯,但卫星设备是 有限的,只能给一部分村庄配备。

现在有 k 台卫星设备,请你编一个程序,计算出应该如何分配这 k 台卫星设备,才能使所配备的无线电收发机的 d 值最小。

例如,对于下面三座村庄:

其中,|AB|=10,|BC|=20,|AC|=105√≈22.36

如果没有任何卫星设备或只有 1 台卫星设备 (k=0 或 k=1),则满足条件的最小的 d=20,因为 A 和 B,B 和 C 可以用无线电直接通讯;而 A 和 C 可以用 B 中转实现间接通讯 (即消息从 A 传到 B,再从 B 传到 C);

如果有 2 台卫星设备 (k=2),则可以把这两台设备分别分配给 B 和 C ,这样最小的 d 可取 10,因为 A 和 B 之间可以用无线电直接通讯;B和 C 之间可以用卫星直接通讯;A 和 C 可以用 B 中转实现间接通讯。

如果有 3 台卫星设备,则 A,B,C 两两之间都可以直接用卫星通讯,最小的 d 可取 0。

输入格式

第一行为由空格隔开的两个整数 n,k;

接下来 n 行,每行两个整数,第 i 行的 xi,yi表示第 i 座村庄的坐标 (xi,yi)。

输出格式

一个实数,表示最小的 d 值,结果保留 2 位小数。

数据范围

1≤n≤500
0≤x,y≤104
0≤k≤100

输入样例:

3 2
10 10
10 0
30 0

输出样例:

10.00

#include<bits/stdc++.h>
using namespace std;
#define x first 
#define y second
const int N=510,M=N*N/2;
int n,k,m;
struct T{
    int a,b;
    double w;
}s[M];
typedef pair<int,int>PII;
PII q[M];
int p[N];
bool cmp(T a,T b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}
double get(PII a,PII b)
{
    int x=a.x-b.x,y=a.y-b.y;
    return sqrt(x*x+y*y);
}
int main()
{
    cin>>n>>k;
    for(int i=0;i<n;i++)cin>>q[i].x>>q[i].y;
    for(int i=0;i<n;i++){
        for(int j=0;j<i;j++){
            s[m++]={i,j,get(q[i],q[j])};
        }
    }
    sort(s,s+m,cmp);
    for(int i=0;i<n;i++)p[i]=i;
    int cnt=n;
    double ans=0;
    for(int i=0;i<m;i++){
        if(cnt<=k)break;
        int a=find(s[i].a),b=find(s[i].b);
        double w=s[i].w;
        if(a!=b){
            p[a]=b;
            cnt--;
            ans=w;
        }
    }
    printf("%.2lf",ans);
    return 0;
}

6 1146 新的开始

发展采矿业当然首先得有矿井,小 FF 花了上次探险获得的千分之一的财富请人在岛上挖了 n 口矿井,但他似乎忘记了考虑矿井供电问题。

为了保证电力的供应,小 FF 想到了两种办法:

  1. 在矿井 i 上建立一个发电站,费用为 vi(发电站的输出功率可以供给任意多个矿井)。
  2. 将这口矿井 i 与另外的已经有电力供应的矿井 j 之间建立电网,费用为 pi,j

小 FF 希望你帮他想出一个保证所有矿井电力供应的最小花费方案。

输入格式

第一行包含一个整数 n,表示矿井总数。

接下来 n 行,每行一个整数,第 i个数 vi 表示在第 i 口矿井上建立发电站的费用。

接下来为一个 n×n的矩阵 P,其中 pi,j 表示在第 i 口矿井和第 j 口矿井之间建立电网的费用。

数据保证 pi,j=pj,i且 pi,i=0

输出格式

输出一个整数,表示让所有矿井获得充足电能的最小花费。

数据范围

1≤n≤300
0≤vi,pi,j≤105

输入样例:

4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0

输出样例:

9
#include<bits/stdc++.h>
using namespace std;
const int N=310;
int n,w[N][N],d[N];
bool st[N];
int prim()
{
    memset(d,0x3f,sizeof d);
    d[0]=0;
    int ans=0;
    for(int i=0;i<=n;i++){
        int t=-1;
        for(int j=0;j<=n;j++){
            if(!st[j]&&(t==-1||d[t]>d[j])){
                t=j;
            }
        }
        st[t]=true;
        ans+=d[t];
        for(int j=0;j<=n;j++)d[j]=min(d[j],w[t][j]);
    }
    return ans;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>w[0][i];
        w[i][0]=w[0][i];
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>w[i][j];
        }
    }
    cout<<prim();
    return 0;
}

 7 1148 秘密的牛奶运输

农夫约翰要把他的牛奶运输到各个销售点。

运输过程中,可以先把牛奶运输到一些销售点,再由这些销售点分别运输到其他销售点。

运输的总距离越小,运输的成本也就越低。

低成本的运输是农夫约翰所希望的。

不过,他并不想让他的竞争对手知道他具体的运输方案,所以他希望采用费用第二小的运输方案而不是最小的。

现在请你帮忙找到该运输方案。

注意:

  • 如果两个方案至少有一条边不同,则我们认为是不同方案;
  • 费用第二小的方案在数值上一定要严格大于费用最小的方案;
  • 答案保证一定有解;

输入格式

第一行是两个整数 N,M表示销售点数和交通线路数;

接下来 M 行每行 3 个整数 x,y,z,表示销售点 x 和销售点 y 之间存在线路,长度为 z。

输出格式

输出费用第二小的运输方案的运输总距离。

数据范围

1≤N≤500
1≤M≤104
1≤z≤109
数据中可能包含重边。

输入样例:

4 4
1 2 100
2 4 200
2 3 250
3 4 100

输出样例:

450

 

#include<bits/stdc++.h>
using namespace std;
const int N=510,M=10010;
int h[N],e[N*2],ne[N*2],w[N*2],idx;
int dist1[N][N],dist2[N][N];
int p[N];
int n,m;
typedef long long LL;
struct T{
    int a,b,w;
    bool f;
}s[M];
void add(int a,int b,int c)
{
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
int find(int x)
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}
bool cmp(T a,T b)
{
    return a.w<b.w;
}
void dfs(int u,int fa,int maxx1,int maxx2,int d1[],int d2[])
{
    d1[u]=maxx1,d2[u]=maxx2;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(j!=fa){
            int t1=maxx1,t2=maxx2;
            if(w[i]>t1)t2=t1,t1=w[i];
            else if(w[i]<t1&&w[i]>t2)t2=w[i];
            dfs(j,u,t1,t2,d1,d2);
        }
    }
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    for(int i=0;i<m;i++){
        int a,b,w;
        cin>>a>>b>>w;
        s[i]={a,b,w};
    }
    sort(s,s+m,cmp);
    LL sum=0;
    for(int i=1;i<=n;i++)p[i]=i;
    for(int i=0;i<m;i++){
        int a=s[i].a,b=s[i].b,w=s[i].w;
        int pa=find(a),pb=find(b);
        if(pa!=pb)
        {
            p[pa]=b;
            sum+=w;
            add(a,b,w),add(b,a,w);
            s[i].f=true;
        }
    }
    for(int i=1;i<=n;i++)dfs(i,-1,-1e9,-1e9,dist1[i],dist2[i]);
    LL ans=1e18;
    for(int i=0;i<m;i++){
        if(!s[i].f){
            int a=s[i].a,b=s[i].b,w=s[i].w;
            LL t;
            if(w>dist1[a][b])t=sum+w-dist1[a][b];
            else if(w>dist2[a][b])t=sum+w-dist2[a][b];
            ans=min(ans,t);
        }
    }
    cout<<ans;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值