POJ_3013_BigChristmasTree ( 最短路变形 | INF爆0x3f3f3f3f | 转化技巧 )

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_44510468/article/details/102711972

题目连接 vj or poj

题意

用所有的顶点( 注意是所有顶点! ), 构建一颗树, 如果不可以, 输出字符串,
否则输出构建的最小花费
不过这个题求边权的时候比较特殊,

某条边的边权 = 此边起点所到达的顶点及其所有子节点(不包括此起点)顶点值的和*此边的单位权值

仔细理解边权的求法, 比较绕, 但是看明白的确是很精妙

题解

这是一个巨坑 ! 巨坑 ! 巨坑 ! 重要的事情说三遍 ! wa了整整一页 !!!
INF 开 0x3f3f3f3f 会小了, 导致 wa !!! INF 开 0x3f3f3f3f 会小了, 导致 wa!!! INF 开 0x3f3f3f3f 会小了, 导致 wa!!!
dis数组 要开 long long , INF开 1e11 或者更大到 1e18, 19可能爆long long

附上样例 2 构建的图, 注意无向图
在这里插入图片描述
按题意要求, 计算最小花费时, 拆分合并一下同类项, 会神奇的发现

某条边的边权 = 此边所到达顶点的权值*1号节点到此顶点的最短路径

如下步骤,
在这里插入图片描述
那么, 我们只需要求出最短路以后, 对 dis[i]*value[i] 加和 即可

代码

Dijkstra+堆优化

在这里插入图片描述

// #include <bits/stdc++.h> 
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std; 
#define rg register 
#define sc scanf 
#define pf printf 
typedef long long ll; 

class Dijkstra_HeapOptimize {
public:
	static const int MAXN = 6e4+10, MAXM = 2e5+10;
	static const ll INF = 1e11 ;
	int N, M, S;
    Dijkstra_HeapOptimize ( ) { };
    Dijkstra_HeapOptimize ( int N, int M, int S ) : N(N), M(M), S(S) { };
	struct EDGE {
		int v, w, to;
	}e[MAXM];
	int head[MAXN]; ll dis[MAXN], cnt; bool vis[MAXN];
	inline void add ( int u, int v, int w ) {
		e[cnt].v = v, e[cnt].w = w, e[cnt].to = head[u]; head[u] = cnt++; 
	}
	struct NODE {
		int nd, w;
		bool operator < ( const NODE& x )const {
			return x.w < w;
		}
	};
	std::priority_queue<NODE> q;
	void ini ( int N, int M, int S ) {
        this->N = N, this->M = M, this->S = S;
		cnt = 0;
		memset( head, -1, sizeof(head) );
		// memset( dis, 0x3f, sizeof(dis) );
        for ( int i = 1; i <= N; ++i ) dis[i] = INF;
		dis[S] = 0; 
		memset( vis, false, sizeof(vis) );
		while ( !q.empty() ) q.pop() ; 
	}
	inline void run ( ) {
		q.push ( (NODE){ S, 0 } );
		while ( !q.empty ( ) ) {
			register int u = q.top( ).nd;
			q.pop ( );
			if ( vis[u] ) continue;
			vis[u] = true;
			for ( int k = head[u]; k!=-1; k = e[k].to ) {
				register int v = e[k].v, w = e[k].w;
				if ( dis[v] > dis[u]+w ) {
					dis[v] = dis[u]+w;
					if ( !vis[v] ) {
						q.push ( (NODE){ v, dis[v] } );
					}
				}
			}
		}
	}
}dj;


int main ( ) {  // freopen( "F:\\in\\.txt" , "r" , stdin ); 

    int T,
        n, m,
        vm[dj.MAXN],
        u, v, w;

    sc( "%d", &T );
    while ( T-- ) {
        sc( "%d%d", &n, &m );

        dj.ini( n, m, 1 );  

        for ( int i = 1; i <= n; ++i ) 
            sc( "%d", &vm[i] );

        for ( int i = 0; i < m; ++i ) {
            // cin >> u >> v >> w;
            sc( "%d%d%d", &u, &v, &w );
            dj.add( u, v, w );
            dj.add( v, u, w );
        }

        dj.run ( );

        ll sum = 0;
        bool flag = true;
        for ( int i = 2; i <= n; ++i ) {
            if ( dj.dis[i] == dj.INF ) {
                flag = false;
                break;
            }
            sum += dj.dis[i] * vm[i];
        }

        if ( flag ) pf( "%lld\n", sum );
        else puts( "No Answer" );

    }




    return 0 ; 
} 

SPFA

在这里插入图片描述

// #include <bits/stdc++.h>
#include <cstdio>
#include <queue>
#include <cstring>
#define sc scanf
#define pf printf
using namespace std;
typedef long long ll;

class SPFA {
public:
	static const int MAXN = 6e4+10, MAXM = 2e5+10;
	static const ll INF = 1e15 ;
	int n, m, s;
	struct EDGE {
		int v, w, to;
	}e[MAXM];
	int head[MAXN], cnt;
	ll dis[MAXN]; bool vis[MAXN];
	inline void add ( int u, int v, int w ) {
		e[cnt].v = v, e[cnt].w = w, e[cnt].to = head[u]; head[u] = cnt++; 
	}
	int tim[MAXN];
	queue<int> q;
	void ini ( int n, int m, int s ) { 
		this->n = n; this->m =m, this->s = s;
		cnt = 0;
		memset ( head, -1, sizeof ( head ) );
		// memset ( dis, 0x3f, sizeof ( dis ) );
        for ( int i = 1; i <= n; ++i ) dis[i] = INF;
		dis[s] = 0;
		memset ( vis, false, sizeof ( vis ) );
		while ( !q.empty ( ) ) q.pop ( );
		memset ( tim, 0, sizeof (tim) );
	}
	void run ( ) {
		q.push( s );
		vis[s] = true;
		tim[s] = 0;
		while ( !q.empty ( ) ) {
			int u = q.front ( );
			q.pop ( );
			vis[u] = false;
			for ( int k = head[u]; k!=-1; k = e[k].to ) {
				int v = e[k].v, w = e[k].w;
				if ( dis[v] > dis[u]+w ) {
					dis[v] = dis[u]+w;
					tim[v] = tim[u]+1;
					if ( !vis[v] ) q.push ( v ), vis[v] = true;
				}
			}
		}
	}
}sp;



int main ( ) {
	int T,
        n, m,
        vm[sp.MAXN],
        u, v, w;

    sc( "%d", &T );
    while ( T-- ) {
        sc( "%d%d", &n, &m );

        sp.ini( n, m, 1 );  

        for ( int i = 1; i <= n; ++i ) 
            sc( "%d", &vm[i] );

        for ( int i = 0; i < m; ++i ) {
            // cin >> u >> v >> w;
            sc( "%d%d%d", &u, &v, &w );
            sp.add( u, v, w );
            sp.add( v, u, w );
        }

        sp.run ( );

        ll sum = 0;
        bool flag = true;
        for ( int i = 2; i <= n; ++i ) {
            if ( sp.dis[i] == sp.INF ) {
                flag = false;
                break;
            }
            sum += sp.dis[i] * vm[i];
        }

        if ( flag ) pf( "%lld\n", sum );
        else puts( "No Answer" );

    }


	return 0;
}

再附上某人一个乱七八糟的 vector模拟邻接表实现 SPFA

在这里插入图片描述

#include<cstdio>
#include<cstring>
#include<queue>

#define INF 1e12

using namespace std;
typedef long long ll;


const int MAX = 5e4+10;

struct edge{
	int to,w;
	//边:起点 from ,终点 to,权值 w ,from 并没有用到, e[i] 的 i 就是 from  
};

vector<edge>e[MAX];  // e[i] : 存第 i 个节点连接的所有边
int n,m;
ll aa[MAX];       //存每个节点的权值 
//int pre[MAX];
//记录前驱节点.pre[x] = y,在最短路径上,节点 x 的前一个结点是 y 

int spfa(int s){
	ll dis[MAX];     //记录所有结点到起点的距离 
	bool inq[MAX];    //inq[i] = true 表示节点 i 在队列中 
//	int Neg[MAX];     //判断负圈(Negative loop) 
//	memset(Neg,0,sizeof(Neg));
//	Neg[s] = 1;
	for(int i = 1; i <= n; i++){  //初始化 
		dis[i] = INF;
		inq[i] = false;
	}
	dis[s] = 0;    //起点到自己的距离是 0  
	queue<int>Q;
	while(!Q.empty()) Q.pop(); 
	Q.push(s);
	inq[s] = true;   //起点入队列
	while(!Q.empty()){
		int u = Q.front();
		Q.pop();   //队列出队
		inq[u] = false;
		for(int i = 0; i < e[u].size(); i++){    //检查节点 u 的所有邻居 
			int v = e[u][i].to,w = e[u][i].w;
			if(dis[u] + w < dis[v]){ //u 的第 i 个邻居 v, 他借道 u 到 s 更近 
				dis[v] = dis[u] + w;  //更新第 i 个邻居到 s 的距离           
				if(!inq[v]){  //第 i 个邻居更新状态了,但是他不在队列中,把它放进队列 
					inq[v] = true;  
					Q.push(v);
//					Neg[v]++;
//					if(Neg[v] > n) return 1; //出现负圈 
				} 
			} 
		}  
	}
	
	int flag = 0;
	for(int i = 1; i <= n; i++){
		if(dis[i] == INF){
			flag = 1;
			break;			
		}
	}
	if(flag == 1){
		printf("No Answer\n");
	}
	else{
		ll sum = 0;
		for(int i = 1; i <= n; i++){
			sum += dis[i]*aa[i];
		}
		printf("%lld\n",sum);
	}     
	return 0; 
}

int main(){
	int T,a,b,c; 
	scanf("%d",&T);
	
	while(T--){
		scanf("%d %d",&n,&m);
		for(int i = 1; i <= n; i++){
			e[i].clear();
		}
		for(int i = 1; i <= n; i++){
			scanf("%lld",&aa[i]);
		}
		for(int i = 1; i <= m; i++){
			scanf("%d %d %d",&a,&b,&c);
			e[a].push_back( (edge) { b,c} ); //结点 a 的邻居,都放在 node[a] 里 
			e[b].push_back( (edge) { a,c} );
		} 
		spfa(1);   //起点是1 
	}
	return 0;
} 
来自 poj discuss的数据
/* 提供水数据。。。
Posted by squarefk at 2012-01-26 01:42:04 on Problem 3013
INPUT:
3
2 1
3 1 
2 2 3
4 10
7 5 1 10 
1 2 9
4 4 2
2 3 5
1 1 1
2 1 4
1 2 1
4 3 5
2 1 6
2 4 9
3 4 4
8 60
6 12 1 6 19 6 6 16 
4 3 160
5 7 87
7 1 46
4 5 190
3 4 156
2 8 96
3 2 193
4 8 8
8 4 126
2 2 52
4 2 68
1 4 119
6 8 182
5 3 188
1 1 160
6 2 8
3 6 171
3 7 147
7 5 46
1 3 43
3 6 36
7 7 103
5 4 69
5 4 128
3 8 40
6 5 118
1 5 51
8 2 35
6 6 108
1 5 80
4 2 5
1 3 84
3 6 34
6 1 94
3 4 112
7 3 57
7 8 179
4 7 138
3 6 74
4 4 161
2 1 81
8 6 7
3 1 73
2 2 58
4 7 35
6 6 79
3 6 40
3 4 136
2 8 178
1 3 127
5 7 137
4 2 123
8 7 163
7 4 124
7 3 20
8 8 200
6 1 164
2 5 119
5 2 99
2 8 171

OUTPUT:
No Answer
111
4536
 */

猜你喜欢

转载自blog.csdn.net/weixin_44510468/article/details/102711972