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村庄邮局问题
题目大意:一系列村庄在一维坐标系上有序的排列,建设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首届大学生算法大赛 - 村庄的主要内容,如果未能解决你的问题,请参考以下文章