2023首届大学生算法大赛 - 村庄

Posted zzc大魔王

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2023首届大学生算法大赛 - 村庄相关的知识,希望对你有一定的参考价值。

 


读题可以发现,如果两个村庄不能互相连通,那就算作一对 (a<b)。

显然是可以用floyd全局多源最短路来做的,如果不存在最短路,那么就是不能互通,但是这道题的数据范围N<=10^5,跑floyd复杂度为O(n^3)即10^15远大于10^8,显然是不行,复杂度级别只能是O(n),线性的,或者O(nlogn)。

看一下样例说明

显然如果两个村庄位于同一个连通块中,必然找不出(a<b),如果不在同一个连通块中,必然会产生(a<b),且数量刚好是第一个连通块的村庄数量 乘以 第二个连通块的数量。

为什么?

假设下图,1和2相连,3和4相连。

 明显可以发现1与3,4,都不相连,产生了第二个连通块村庄数量的(a<b)

2与3,4,也不相连,即为2*2=4。

所以直接使用并查集:【模板】并查集 - 洛谷

初始给每个村庄都分配一个连通块,如果有桥相连,就把它们加入同一个连通块。

因为会摧毁编号为1~k的桥,所以输入的时候只需要i>k的部分,不需要合并被摧毁的部分。

 

细节(以样例说明):第四座桥把4接到了连通块1上,第五座桥把1接到了连通块3上,这里就出现了一个问题,4并没有被更新,所以最后还要再跑一遍find,更新所有情况。(本蒟蒻考试的时候熬夜昏迷了,没想到这一点)

应该能AC的代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,m,k,u,v,f[100001],cnt,ans;
bitset<100001>vis;
int a[100001];//连通块编号,其中村庄数量
vector<int>a_;
//定义f[i]=j为:i村庄在连通块j中
int find(int x)
    if(f[x]==x)return x;
    else return f[x]= find(f[x]);

void unit(int x,int y)
    x= find(x),y= find(y);
    f[x]=y;


signed main()
    ios::sync_with_stdio(false);
    cin.tie(nullptr),cout.tie(nullptr);
    cin>>n>>m>>k;
    for(int i=1;i<=n;++i)//初始化每个村庄的连通块
        f[i]=i;
    for(int i=1;i<=m;++i)
        cin>>u>>v;
        if(i>k)//1~k的桥全部被摧毁,只需要i>k的桥
            unit(u,v);
        
    

    for(int i=1;i<=n;++i)//跑一遍find,更新最终情况
        find(i);
    for(int i=1;i<=n;++i)//累计连通块编号
        a[f[i]]++;
    
    for(int i=1;i<=n;++i)//把累计完的连通块全部拿出来
        if(a[i])a_.push_back(a[i]);
    for(int i=0;i<a_.size();++i)//配对连通块,累计答案
        for(int j=i+1;j<a_.size();++j)
            ans+=a_[i]*a_[j];

    cout<<ans;
    return 0;

如果有错误或者有更好的思路,欢迎评论!

看看算法大赛还会不会开放OJ让我们再次测试。

同时预祝各位在2023/4/8的蓝桥杯省赛中while(true)rp++

别熬夜 : )

 

区间DP POJ1160村庄邮局问题

POJ1160

题目大意:一系列村庄在一维坐标系上有序的排列,建设p个邮局,问各个村庄到邮局的最短距离和。

线性区间DP问题

dp数组的值为最短/最优距离

村庄和邮局为限制

dp[i][j]表示前i个村庄有j个邮局的最优解

分析最小子问题可得前i个村庄有1个邮局的最优解——中间的村庄

所以分解区间dp[i][j] = min(dp[i][j],dp[k][j-1] + sum[k+1][i])

sum[i][j]数组表示从i到j村庄放一个邮局的最优解

j,i,k的遍历,最终得到最优解

外层遍历的是邮局的个数,这样才能遍历村庄的个数(我们所未知的情况是村庄的个数大于邮局的个数)

对了sum【i】【j】和sum[i][j-1]存在某种关系呦,一推就出来了~~

#include <iostream>
#include <cstdio>
#include <string.h>
#define inf (1e8 + 9e7)
using namespace std;
const int maxn = 1e3 + 1e2;
int dp[maxn][maxn];
int sum[maxn][maxn];
int pos[maxn];
void init()
{
    memset(sum,0,sizeof(sum));
}
int main()
{
    int n,pn;
    while(~scanf("%d%d",&n,&pn))
    {
        init();
        for(int i = 1;i <= n;i++)
            scanf("%d",&pos[i]);

        for(int i = 1;i <= n;i++)
        {
            for(int j = i+1;j <= n;j++)
            {
                sum[i][j] = sum[i][j-1] + pos[j] - pos[(i + j)/2];
            }
            dp[i][i] = 0;
        }

        for(int i = 1;i <= n;i++)
        {
            dp[i][1] = sum[1][i];
        }

        for(int j = 2;j <= pn;j++)//建造一个的情况已经铺垫好了
            for(int i = j+1;i <= n;i++)//村庄首先必须保证得比
            {
                //接下来我要做的是遍历k来找到到底几个村庄放j-1个最优
                dp[i][j] = inf;
                for(int k = j - 1;k < i;k++)
                {
                    dp[i][j] = min(dp[i][j],dp[k][j-1] + sum[k+1][i]);
                }
            }
        printf("%d
",dp[n][pn]);
    }
    return 0;
}

 

以上是关于2023首届大学生算法大赛 - 村庄的主要内容,如果未能解决你的问题,请参考以下文章

学生强则国强,访天猫推荐算法大赛Top 9团队

修复公路(并查集)

卿学姐与诡异村庄(并查集)

HDU 3635 并查集

cdoj 1328 卿学姐与诡异村庄 Label:并查集 || 二分图染色

并查集的一般操作 ①