P8794 [蓝桥杯 2022 国 A] 环境治理
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100 + 10;
int n, q;
long long d[N][N], l[N][N];
long long tmp[N][N];
bool check(long long val) {
long long sum = 0;
for (int i = 1; i <= n; i++) {
long long t = val / n;
if (val % n + 1 > i) t++;
for (int j = 1; j <= n; j++) {
tmp[i][j] = max(l[i][j], d[i][j] - t);
}
}
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
tmp[i][j] = min(tmp[i][j], tmp[i][k] + tmp[k][j]);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
sum += tmp[i][j];
}
return (sum <= q);
}
int main() {
cin >> n >> q;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> d[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> l[i][j];
long long l = 0, r = 1e5 + 10;
long long ans = 1e5 + 10;
while (l <= r) {
long long mid = (l + r) / 2;
if (check(mid)) {
ans = min(ans, mid);
r = mid - 1;
} else {
l = mid + 1;
}
}
if (ans > (int)1e5) cout << "-1";
else cout << ans;
return 0;
}
解析:
每次二分更新边权,再跑一遍最短路就可以了。
P3367 【模板】并查集
代码:
#include<bits/stdc++.h>
using namespace std;
int i,j,k,n,m,s,ans,f[10010],p1,p2,p3;
//f[i]表示i的集合名
int find(int k){
//路径压缩
if(f[k]==k)return k;
return f[k]=find(f[k]);
}
int main()
{
cin>>n>>m;
for(i=1;i<=n;i++)
f[i]=i;//初始化i的老大为自己
for(i=1;i<=m;i++){
cin>>p1>>p2>>p3;
if(p1==1)
f[find(p2)]=find(p3);
//p3打赢了p2
else
if(find(p2)==find(p3))
//是否是一伙的
printf("Y\n");
else
printf("N\n");
}
return 0;
}
解析:
模板
P8604 [蓝桥杯 2013 国 C] 危险系数
代码:
#include<bits/stdc++.h>
#define LL long long
#define made return
#define in 0
#define China ;
using namespace std;
LL n,m,u,v,ans,cnt[1010],sum;
bool bj[1010],a[1010][1010];
void dfs(LL now){
if(now==v){//如果走到终点了,
sum++;//路径总数加一。
for(int i=1;i<=n;i++)
if(bj[i]==1)cnt[i]++;//每个被走过的点,被走总次数加一
}
else{
for(int i=1;i<=n;i++)
if(a[now][i]==1&&bj[i]==0){//如果两点连通且下一步要走到的点未被走过,
bj[i]=1;//标记。
dfs(i);
bj[i]=0;//回溯一步。
}
}
}
int main(){
scanf("%lld%lld",&n,&m);
while(m--){
scanf("%lld%lld",&u,&v);
a[u][v]=a[v][u]=1;//输入邻接矩阵。因为是无向的,所以u到v和v到u都要设为1。
}
scanf("%lld%lld",&u,&v);
dfs(u);
if(sum>0){//dfs求解
for(int i=1;i<=n;i++)
if(cnt[i]==sum)ans++;//如果这个点被走过的总次数与路径总数相等,那么删去这个点起点与终点间一定不连通。
printf("%lld",ans-1);//因为起点也被算在内,所以总危险系数要减去起点的1。
}
else printf("-1");//如果询问的两点无路径连通则输出'-1'。
made in China
}
//made in China. 中国制造。
解析:
可以使用 dfs(深度优先搜索)求解,求出 u 到 v 间的每一条路径,将路径总数统计,并将被经过的点被经过总数加一。如果一个点被经过的次数与总路径条数相等,那么这一个点就是 u 和 v 的关键点。
P1330 封锁阳光大学
代码:
#include<bits/stdc++.h>
using namespace std;
int f[10001],a,b,n,m,t[10001],bj[10001],h[10001],ans;
int find(int x)//模板函数;
{
if(f[x]!=x) f[x]=find(f[x]);
return f[x];
}
void xx(int x,int y)//判断函数;
{
int qq=find(x);
if(qq!=y)//如果他们父亲不相等将他们合并;
{
f[y]=qq;
t[qq]+=t[y];
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
f[i]=i;
t[i]=1;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
int x1=find(a),x2=find(b);
if(x1!=x2)//相邻两点一定异色;
{
if(h[a]) xx(h[a],x2);/*a的父亲节点一定
和a异色,一定和a的异色点同色,所以将他们
合并;*/
if(h[b]) xx(h[b],x1);//同上;
h[a]=x2;//h数组存a点异色点;
h[b]=x1;//同上;
}
else//如果他们同色,表示不行;
{
cout<<"Impossible";
return 0;
}
}
for(int i=1;i<=n;i++)
{
int q=find(i);
if(!bj[q])//表示这个集合的答案还没被选过;
{
int q1=find(h[i]);
bj[q]=1;
bj[q1]=1;
ans+=min(t[q],t[q1]);//两种情况最小值;
}
}
cout<<ans;
return 0;
}
解析:
并查集
P3916 图的遍历
代码:
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
#define MAXL 100010
int N, M, A[MAXL];
vector<int> G[MAXL]; //vector存图
void dfs(int x, int d) {
if(A[x]) return; //访问过
A[x] = d;
for(int i=0; i<G[x].size(); i++)
dfs(G[x][i], d);
}
int main() {
int u, v;
scanf("%d%d", &N, &M);
for(int i=1; i<=M; i++) {
scanf("%d%d", &u, &v);
G[v].push_back(u); //反向建边
}
for(int i=N; i; i--) dfs(i, i);
for(int i=1; i<=N; i++) printf("%d ", A[i]);
printf("\n");
return 0;
}
解析:
按题目来每次考虑每个点可以到达点编号最大的点,不如考虑较大的点可以反向到达哪些点
循环从N到1,则每个点i能访问到的结点的A值都是i
每个点访问一次,这个A值就是最优的,因为之后如果再访问到这个结点那么答案肯定没当前大了
P1119 灾后重建
代码:
#include<iostream>
#include<cstdio>
#define N 205
using namespace std;
int n,m;
int a[N];
int f[N][N];//邻接矩阵存边
inline void updata(int k){
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(f[i][j]>f[i][k]+f[j][k])
f[i][j]=f[j][i]=f[i][k]+f[j][k];//用这个新的更新所有前面的
return;
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++)
scanf("%d",a+i);//依次输入每一个村庄建立完成时需要的时间
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
f[i][j]=1e9;//初始化为保证它不爆炸范围内的最大值
}
for(int i=0;i<n;i++)
f[i][i]=0;
int s1,s2,s3;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&s1,&s2,&s3);
f[s1][s2]=f[s2][s1]=s3;//初始化边长
}
int q;
cin>>q;
int now=0;
for(int i=1;i<=q;i++){//处理各询问
scanf("%d%d%d",&s1,&s2,&s3);
while(a[now]<=s3&&now<n){
updata(now);//依次更新点,使它可以被用来更新其他的点
now++;
}
if(a[s1]>s3||a[s2]>s3)cout<<-1<<endl;
else {
if(f[s1][s2]==1e9)cout<<-1<<endl;
else cout<<f[s1][s2]<<endl;
}
}
return 0;
}
解析:
Floyd算法