2023NEUQACM Week8

必做题


B3647 【模板】Floyd

说是Floyd模板题,但我还是用了Dijkstra算法。
完整代码如下:

#include<bits/stdc++.h>
using namespace std;
#define M 105
#define inf 0x7FFFFFFF
int n,m,u,v,w,Min,nex;
int Map[M][M];
int Dist[M];
bool vis[M];
int main()
{
    // freopen("D:\\test.txt","r",stdin);
    cin>>n>>m;
    for(int i=1 ; i<=n ; i++) fill(Map[i]+1,Map[i]+1+n,inf);
        // for(int j=1 ; j<=n ; j++) Map[i][j] = inf;
    for(int i=0 ; i<m ; i++){
        cin>>u>>v>>w;
        Map[u][v] =  min(Map[u][v],w);
        Map[v][u] = Map[u][v];
    }
    for(int s=1 ; s<=n ; s++){
        // for(int a=1 ; a<=n ; a++) vis[a]=1;
        fill(vis+1,vis+1+n,1);
        // for(int a=1 ; a<=n ; a++) Dist[a]=inf;
        fill(Dist+1,Dist+1+n,inf);
        int tmp = n;
        int start = s;
        Dist[start]=0;
        vis[start]=0;
        while(--tmp){
            Min = inf;
            for(int i=1 ; i<=n ; i++){
                if(Map[start][i]!=inf) 
                    Dist[i]=min(Dist[i],Dist[start]+Map[start][i]);
                if(vis[i] && Dist[i]<Min){
                    nex = i;
                    Min = Dist[i];
                }
            }
            // if(Min==inf) break;
            start = nex;
            vis[start]=0;
        }
        for(int i=1 ; i<=n ; i++) cout << Dist[i] << " ";
        cout << '\n';
    }
    return 0;
}

P4779 【模板】单源最短路径(标准版)

关键词:链式前向星、堆优化Dijkstra
思路:使用小根堆优化的 Dijkstra 算法(利用priority_queue)
对起始点的所有边都遍历一遍后,将成功以更近的方式到达目标点的情况记录下来,以目前从起始点到达各个终点的总距离由小到大的方式进行排序(入优先队列),其中最短的一条路径就是该终点的最短路径(数学证明略)。这样,我们每次都能确定一个点的最短距离,记录到 dij 数组里,然后再以这个点为起点(出队),遍历以它为起点的所有边(此时之前更长一些的路径还在队列里,要和它一起比较看谁更短),入队,我们就又能确定第二近的点及其总路程,如此往复,直到队列里没有点了,所有的最短路径就都找到了。
完整代码如下:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x7FFFFFFF
const int M = 2e5+5;
const int N = 1e5+5;
int n,m,s,u,v,w,nex,cnt=0;    
int dij[N],head[N];
bool vis[N];
struct edge{
    int to,nex,wei;
}e[M];
void add_edge(int ui, int vi, int wi)
{
    e[++cnt].to = vi; // 从编号1开始记录边,不初始化head(这样默认就是0),head[i]为0说明没有边了
    e[cnt].wei = wi;
    e[cnt].nex = head[ui];
    head[ui] = cnt;
}
struct node{
    int from,wei; // 记录起点,同一起点的按wei从小到大排序,这里的wei是到这个点时总共走的路程
    bool operator < (const node &x)const{ // 重载小于号,设置优先级(不要忘了大括号前的const)
        return x.wei < wei;
    }
}tmp;
priority_queue<node> pq;
void dijkstra()
{
    dij[s]=0;
    pq.push((node){s,0});
    while(!pq.empty()){
        tmp = pq.top();
        pq.pop();
        if(vis[tmp.from]) continue;
        // tmp.from为起点的点已经看过了,队列后面的都不是tmp.from的最优解,直接删掉跳过
        vis[tmp.from] = true;
        for(int i=head[tmp.from] ; i ; i=e[i].nex){
            if(dij[e[i].to] > dij[tmp.from]+e[i].wei){
                dij[e[i].to] = dij[tmp.from]+e[i].wei;
                pq.push((node){e[i].to,dij[e[i].to]});
            }
        }
    }
}
int main()
{
    // freopen("D:\\test.txt","r",stdin);
    cin>>n>>m>>s;
    fill(dij+1,dij+1+n,inf);
    for(int i=0 ; i<m ; i++){
        cin>>u>>v>>w;
        add_edge(u,v,w);
    }
    dijkstra();
    for(int i=1 ; i<=n ; i++) cout << dij[i] << " ";
    return 0;
}

P2661 [NOIP2015 提高组] 信息传递

关键词:并查集、路径压缩
很不巧的是,我不会路径压缩,所以只能暴力求解
思路:如果两个点有公共的祖先,那么这两个点在相连后一定会形成一个环(因为每个点只会指向一个父节点),先初始化所有的点的祖先为自己,把各个点的父节点存到一个数组里,然后遍历每个点,当该点和其父节点不在同一个集合中时,将这个点的父节点设置为他的父节点;如果该点和其父节点在同一个集合中,我们从这个父节点一路向前找,一定能找到该子节点,记下路程。
本地跑不过,但是交上去能过……
完整代码如下:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x7FFFFFFF
const int N = 2e5+5;
int n,fp,cnt,fa[N],ans=inf;
int find(int k, int &cnt){
	++cnt;
	return k==fa[k] ? k : find(fa[k], cnt);
}
int main(){
    // freopen("D:\\test.txt","r",stdin);
	cin>>n;
	for(int i=1 ; i<=n ; i++) fa[i]=i;
	for(int i=1 ; i<=n ; i++){
		cin>>fp;
		cnt=0;
		i==find(fp,cnt) ? ans=min(ans,cnt) : fa[i]=fp;
	}			
	cout<<ans;
	return 0;
}

Update 12/27/2023

实现了路径压缩。
完整代码如下:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x7FFFFFFF
const int N = 2e5+5;
int n,fp,cnt,fa[N],len[N],ans=inf,tmp;
int find(int k, int &cnt){
	if(fa[k]==k) return k;
    tmp = find(fa[k],cnt); // 调用函数,并在最后记录祖先节点
    len[k] += len[fa[k]]; // 记录下从此点到祖先的距离
    fa[k] = tmp; // 压缩高度,此时我们已经用len记录下了此点到祖先的距离,这个环已经没有用了
    cnt = len[k] + 1;
    return fa[k];
}
int main(){
    // freopen("D:\\test.txt","r",stdin);
	scanf("%d",&n);
	for(int i=1 ; i<=n ; i++) fa[i]=i;
	for(int i=1 ; i<=n ; i++){
		scanf("%d",&fp);
		cnt=0;
		if(find(fp,cnt)==i) ans = min(ans, cnt);
        else{
            len[i] = 1; // 说明在第21行的find函数没能找到一个环,我们要把len[i]设置成1
            fa[i] = fp; // 在图上加一个点,如果找到环了就不能连在图上了,不然会死循环
        }
	}			
	printf("%d",ans);
	return 0;
}

P1144 最短路计数

思路:使用堆优化 Dijkstra 算法,所有边的权重都是 1 1 1,开一个记录答案数量的数组 ans,当搜到一个点时,有三种情况:路程大于先前的,直接跳过;路程等于先前的, a n s + 1 ans+1 ans+1;路程小于先前的, a n s p r e = a n s n o w ans_{pre}=ans_{now} anspre=ansnow
完整代码如下:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x7FFFFFFF
#define mod 100003
const int N = 1e6+5;
const int M = 2e6+5;
int cnt=0,n,m,ui,vi;
int head[N],ans[N],dij[N];
bool vis[N];
struct edge{
    int to,nex;
}e[M];
struct node{
    int from,wei;
    bool operator < (const node &x)const{
        return x.wei < wei;
    }
}tmp;
void add_edge(int u,int v)
{
    e[++cnt].to = v;
    e[cnt].nex = head[u];
    head[u] = cnt;
}
priority_queue<node> pq; // 队列存起点
void dijkstra()
{
    dij[1]=0;
    pq.push({1,0});
    while(!pq.empty()){
        tmp = pq.top();
        pq.pop();
        if(vis[tmp.from]) continue;
        vis[tmp.from] = true;
        for(int i=head[tmp.from] ; i ; i=e[i].nex){
            if(dij[e[i].to] > dij[tmp.from]+1){
                ans[e[i].to] = ans[tmp.from];
                dij[e[i].to] = dij[tmp.from]+1;
                pq.push({e[i].to,dij[e[i].to]});
            }
            else if(dij[e[i].to] == dij[tmp.from]+1){
                ans[e[i].to] += ans[tmp.from];
                ans[e[i].to] %= mod;
                // 这个点已经被push过了,不用再push了
            }
        }
    }    
}
int main()
{
    // freopen("D:\\test.txt","r",stdin);
    cin>>n>>m;
    fill(dij+1,dij+1+n,inf);
    ans[1]=1;
    for(int i=0 ; i<m ; i++){
        cin>>ui>>vi;
        add_edge(ui,vi);
        add_edge(vi,ui);
    }
    dijkstra();
    for(int i=1 ; i<=n ; i++) cout << ans[i] << '\n';
    return 0;
}

P8794 [蓝桥杯 2022 国 A] 环境治理

思路:二分查找治理天数(注意二分的上界,实测1e7能过),要优化一下治理的过程防止超时!
完整代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef vector<int> v;
typedef vector<vector<int>> vv;
#define inf 0x7FFFFFFF
const int N = 105;
int n,Q;
bool vis[N];
vv Dust(N,v(N)), Lim(N,v(N));
int dijkstra(vv DustA)
{
    int nex,Min,P=0;
    v dij(N);
    for(int s=0 ; s<n ; s++){
        fill(dij.begin(),dij.begin()+n,inf);
        fill(vis,vis+n,false);
        int start = s;
        int tmp = n;    
        vis[start] = true;
        dij[start] = 0;
        while(tmp--){
            Min = inf;
            for(int i=0 ; i<n ; i++){
                if(!vis[i]){
                    dij[i] = min(dij[i],dij[start]+DustA[start][i]);
                    if(dij[i]<Min){
                        nex = i;
                        Min = dij[i];
                    }
                }
            }
            start = nex;
            vis[start] = true;
        }
        for(int i=0 ; i<n ; i++) P += dij[i];
    }
    return P;
}
int improve(int x)
{
    vv DustA = Dust;
    int cir = x/n;
    int cit = x%n;
    for(int i=0 ; i<cit ; i++){
        for(int j=0 ; j<n ; j++){
            DustA[i][j] = max(Lim[i][j], DustA[i][j]-cir-1);
            DustA[j][i] = DustA[i][j];
        }
    }
    for(int i=cit ; i<n ; i++){
        for(int j=0 ; j<n ; j++){
            DustA[i][j] = max(Lim[i][j], DustA[i][j]-cir);
            DustA[j][i] = DustA[i][j];
        }
    }
    return dijkstra(DustA);
}
int Bi_Search()
{
    int hdr=0, mg=1e7, mid, ans=-1, P;
    while(hdr<=mg){
        mid = hdr + (mg-hdr)/2;
        P = improve(mid);
        if(P>Q) hdr = mid+1;
        else{
            mg = mid-1;
            ans = mid;
        }
    }
    return ans;
}
int main()
{
    // freopen("D:\\test.txt","r",stdin);
    scanf("%d%d",&n,&Q);
    for(int i=0 ; i<n ; i++)
        for(int j=0 ; j<n ; j++) scanf("%d",&Dust[i][j]);
    for(int i=0 ; i<n ; i++)
        for(int j=0 ; j<n ; j++) scanf("%d",&Lim[i][j]);
    printf("%d", Bi_Search());
    return 0;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值