【模拟赛】【最短路】险路勿近

15 篇文章 1 订阅
1 篇文章 0 订阅

题目描述

给出一个无向图,求出经过1的最小环

思路


这是某dalao的给的思路
这里做一下详细的补充
二进制优化:
因为每一条边都有唯一一个二进制
所有我们可以枚举二进制的每一位
若此位为0则给到1的出边,若为1则给到1的入边
然后将入1的边连向n+1(新),跑一边dij就行了

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define ll long long
using namespace std;

struct wh_
{
	ll w, h, k, f;
}wh[10000250];

struct wh__
{
	ll k, id;
	bool operator < (const wh__ i)const
	{
		return (k != i.k) ? k < i.k : id < i.id;
	}
};

priority_queue<wh__>hy;

ll Bol[500250], h[500250], Fla[10000250], B[500250];
ll dis[500250], log[10000250], Ru[500250];
ll n, m, tot, x, y, z1, z2, t, Ans;

inline ll read()
{
	ll aba = 0, bab = 1; char c = getchar();
	while(c < '0' || c > '9'){if(c == '-')bab = -1;c = getchar();}
	while('0' <= c && c <= '9')aba = aba * 10 + c - 48, c = getchar();
	return aba * bab;
}

void write(ll aba)
{if(aba > 9)write(aba / 10); putchar(aba % 10 + '0');}

ll Dij()
{
	memset(dis, 0x7f, sizeof(dis));
	memset(B, 0, sizeof(B));
	dis[1] = 0;
	hy.push((wh__){0, 1});
	while(hy.size())
	{
		ll k = hy.top().id; hy.pop();
		if(B[k])continue; B[k] = 1;
		for(ll i = h[k]; i; i = wh[i].h)
		{
			if(wh[i].w == 1)continue;
			if(wh[i].f == 1 && !Fla[i])continue;
			if(dis[wh[i].w] > dis[k] + wh[i].k && !B[wh[i].w])
			{
				dis[wh[i].w] = dis[k] + wh[i].k;
				hy.push((wh__){-dis[wh[i].w], wh[i].w});
			}
		}
	}
	return dis[n + 1];
}

void hw(ll x, ll y, ll z)
{wh[++t] = (wh_){y, h[x], z, x}; h[x] = t;}

int main()
{
	n = read(), m = read();
	
	log[1] = 0;
	for(ll i = 2; i <= 2 * m; ++i)
		log[i] = log[i >> 1] + 1;
		
	for(ll i = 1; i <= m; ++i)
	{
		x = read(), y = read();
		z1 = read(), z2 = read();
		hw(x, y, z1), hw(y, x, z2);
	}
	Ans = 1e9;
	for(ll i = 0; i <= log[t]; ++i)//枚举位数
	{
		memset(Bol, 0, sizeof(Bol));
		memset(Fla, 0, sizeof(Fla));
		memset(Ru, 0, sizeof(Ru));
		tot = 0;
		for(ll j = 1; j <= t; ++j)
		{
			if((j & (1 << i)) && wh[j].f == 1 && !Bol[wh[j].w])
				Fla[j] = 1, Bol[wh[j].w] = 1;
			else if(!(j & (1 << i)) && wh[j].w == 1 && !Bol[wh[j].f])
				Ru[++tot] = j, wh[j].w = n + 1, Bol[wh[j].f] = 1;//连向n+1
		}
		Ans = min(Ans, Dij());
		for(ll j = 1; j <= tot; ++j)wh[Ru[j]].w = 1;//连回1
		memset(Bol, 0, sizeof(Bol));
		memset(Fla, 0, sizeof(Fla));
		memset(Ru, 0, sizeof(Ru));
 		tot = 0;
		for(ll j = 1; j <= t; ++j)
		{
			if(!(j & (1 << i)) && wh[j].f == 1 && !Bol[wh[j].w])
				Fla[j] = 1, Bol[wh[j].w] = 1;
			else if((j & (1 << i)) && wh[j].w == 1 && !Bol[wh[j].f])
				Ru[++tot] = j, wh[j].w = n + 1, Bol[wh[j].f] = 1;
		}
		Ans = min(Ans, Dij());
		for(ll j = 1; j <= tot; ++j)wh[Ru[j]].w = 1;
	}
	write(Ans) ;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值