ACM算法题第八周

第一题:

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,d[105][105];
int main(){
    cin>>n>>m;
    memset(d,100000005,sizeof(d));
    for(int i=0;i<=n;i++) d[i][i]=0;
    for(int i=1;i<=m;i++){
        int j,k,w;
        cin>>j>>k>>w;
        d[j][k]=w;
        d[k][j]=w;
    }
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(d[i][j]>d[i][k]+d[k][j]) d[i][j]=d[i][k]+d[k][j];
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cout<<d[i][j]<<' ';
        }
        cout<<endl;
    }
    return 0;
}

解题思路:

i到j可以认为是i到k的距离加上k到j的距离,转移就是fk,u,v​=min(f k−1,u,v​,f k−1,u,k​+f k−1,k,v​)

第二题:

代码·:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
struct node
{
    int u;//代表节点编号;
    int l;//代表到起点的距离
    node(int _u=0,int _l=0) : u(_u),l(_l) {};
};
bool operator <(node a,node b)
{
    return a.l>b.l;
}
struct edge//储存边信息
{
    int v;//代表终点
    int w;//代表边权
    edge(int _v=0,int _w=0) : v(_v),w(_w) {}
};
vector<edge>e[200001];//储存边信息
int book[100001];
int dis[100001];
int n;
void dijkstra(int s)
{
    for(int i=1; i<=n; i++)
    {
        dis[i]=INF;
        book[i]=0;
    }
    dis[s]=0;
    priority_queue<node>que;
    que.push(node(s,0));
    while(!que.empty())
    {
        node top=que.top();
        que.pop();
        int u=top.u;//u代表top的编号
        if(!book[u])
        {
            book[u]=1;
        }
        else
        {
            continue;
        }
        for(int i=0; i<e[u].size(); i++)
        {
            int v=e[u][i].v;
            int w=e[u][i].w;
            if(!book[v]&&dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                que.push(node(v,dis[v]));
            }
        }
    }
}
int main()
{
    int m,s;
    cin>>n>>m>>s;
    for(int i=1; i<=m; i++)
    {
        int u,v,w;
        cin>>u>>v>>w;
        e[u].push_back(edge(v,w));
    }
    dijkstra(s);
    for(int i=1; i<=n; i++)
    {
        if(i==1)
        {
            cout<<dis[i];
        }
        else
        {
            cout<<" "<<dis[i];
        }
    }
    cout<<endl;
    return 0;
}

解题思路:

 Dijkstra 的本质就是,每次找一个 dis 最小的点,然后去更新周围的点。

一个很好的性质是,每个点只会更新一次其它的点。

每次暴力找 dis 最小的点是 O(n2+m) 的,但是我们可以开一个堆存下所有 (u,dis) 二元组,每次找一个dis 最小的即可。这样子复杂度就优化到了O((n+m)logm)。

第三题:

代码:

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 200010;
int n, fa[N], ans = 0x3f3f3f3f;
int get (int x, int &cnt) { //cnt记录环的长度 
    cnt ++;
    if (fa[x] == x) return x;
    else return get(fa[x], cnt);
}
int main () {
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++)
        fa[i] = i;
    for (int i = 1; i <= n; i ++) {
        int cnt = 0, f;
        scanf("%d", &f);
        if (get(f, cnt) == i) {
            ans = min(ans, cnt); //维护最小的环 
        }else
            fa[i] = f;
    }
    printf("%d", ans);
    return 0;
}

解题思路:

把每个同学看成一个点,信息的传递就是在他们之间连有向边,游戏轮数就是求最小环。

图论求最小环,我在里面看到了并查集。

假如说信息由A传递给B,那么就连一条由A指向B的边,同时更新A的父节点,A到它的父节点的路径长也就是B到它的父节点的路径长+1。

这样我们就建立好了一个图,之后信息传递的所有环节都按照这些路径。游戏结束的轮数,也就是这个图里最小环的长度。

如果有两个点祖先节点相同,那么就可以构成一个环,长度为两个点到祖先节点长度之和+1。

第四题:

代码:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
using namespace std;

const int maxn=1000000+1,maxm=2000000+1,INF=0x7f7f7f7f,MOD=100003;
vector<int>G[maxn];int dep[maxn];bool vis[maxn];int cnt[maxn];

int main(){
    int N,M;scanf("%d%d",&N,&M);
    for(int i=1;i<=M;i++){
        int x,y;scanf("%d%d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    queue<int>Q;dep[1]=0;vis[1]=1;Q.push(1);cnt[1]=1;
    while(!Q.empty()){
        int x=Q.front();Q.pop();
        for(int i=0;i<G[x].size();i++){
            int t=G[x][i];
            if(!vis[t]){vis[t]=1;dep[t]=dep[x]+1;Q.push(t);}
            if(dep[t]==dep[x]+1){cnt[t]=(cnt[t]+cnt[x])%MOD;}
        }
    }
    for(int i=1;i<=N;i++){
        printf("%d\n",cnt[i]);
    }
    return 0;
}

解题思路:

只要起点到一个点的距离有几种不同走法但是所过路径权值和相等就说明到这个点的最短路种数不同

也就是说,要求A到B的最短路的方案数,也就是说求A到B有多少条路径是最短且总权值相等的

根据最短路算法的方式,他是根据前面点最短路的情况来决定后面点的情况的。

那么如果我们要求当前点的方案数,我们就需要分两种情况 第一种是到当前点的最短路需要更新:

当前的方案数也就是中间点v的方案数

第二种就是当前的到B的最短路不需要更新,即出现 

dis[a[k].to]=dis[v]+a[k].dis

由于出现了与之前中间点不同的最短路相同的情况,则到当前B点的方案数加上这个中间点的方案数

注意:第一种情况的改变是通过替换,第二种情况是累加!

其他的都是一样的,根据SPFA的模板套一下即可

第五题:

代码:

#include<bits/stdc++.h>
using namespace std;
#define N 105
#define int long long
#define inf 2002102828
#define mid (l + r >> 1)
#define For(i, j, n) for(int i = j ; i <= n ; ++i)
int n, Q, l, r, ans = inf;
int D[N][N], L[N][N], dis[N][N], down[N];

inline int check(int x){
    int t = x / n, P = 0;
    For(i, 1, n) For(j, 1, n) dis[i][j] = inf;
    For(i, 1, n) dis[i][i] = 0;
    For(i, 1, n) down[i] = t;
    For(i, 1, x - n * t) ++down[i];
    For(i, 1, n) For(j, 1, n) dis[i][j] = max(L[i][j], D[i][j] - down[i] - down[j]);
    For(k, 1, n) For(i, 1, n) For(j, 1, n) dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
    For(i, 1, n) For(j, 1, n) P += dis[i][j];
    if(P <= Q) return 1;
    else return 0;
}

signed main(){
    scanf("%lld %lld", &n, &Q);
    For(i, 1, n) For(j, 1, n) scanf("%lld", &D[i][j]);
    For(i, 1, n) For(j, 1, n) scanf("%lld", &L[i][j]);
    l = 0, r = inf;
    while(l <= r){
        if(check(mid)) ans = min(ans, mid), r = mid - 1;
        else l = mid + 1;
    }
    if(ans == inf) printf("-1");
    else printf("%lld", ans);
    return 0;
}

解题思路:

用二分法来求最少要经过多少天后 P 指标可以满足要求。

初始定义治理需要的最少天数 left=0,治理需要的最多天数可设为每个城市治理十万天,置 right=100000×n,取 left 和right 的中值mid,计算经过mid 天治理后的 P 指标值。若 P 指标值满足要求,说明还有减少治理天数的希望,置right=mid−1;若 P 指标值不满足要求,说明治理的天数不够,需要增加治理天数,置 left=mid。

在计算治理mid 天后的 P 指标值时,采用 Floyed 算法求任意两个城市间的最短距离(最小的灰尘度的值)。

  • 23
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值