[FROM LUOGU]P5022 旅行

本文详细解析了NOIP中一道关于旅行的算法题,重点介绍了如何处理基环树结构,通过DFS深度优先搜索找到最小字典序遍历路径。文章分享了60分和100分的解题思路,并提供了完整的代码实现。

P5022 旅行

传送门
SOL
NOIP的题向来废话多,说白了就是求遍历整张图的最小字典序,然而这是个基环树!
60 p t s 60pts 60pts:用vector存一下每个点的连边,排个序Dfs即可
100 p t s 100pts 100pts:有环存在?可以确定最多就一个环,考虑枚举删除环上的每一条边,然后Dfs,比较得出最小字典序遍历再输出
有些民间数据有些卡常,我之前在本校OJ上T了很久都是93分,后来评测机变好之后突然就过了,wsm……
代码很丑,敬请见谅

代码:

#include<bits/stdc++.h>
#define re register
using namespace std;
const int N=5005;
int n,m,fa[N],ans=1,tot,path[N][N],top,ring[N];
bool vis[N];
vector<int>e[N];
inline bool _ring(int k){
	for(int re i=1;i^n;++i){
		if(!path[k][i])return 0;
		if(path[ans][i]!=path[k][i])return path[k][i]<path[ans][i];
	}return 0;
}
inline char nc(){
    static char buf[N],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,N,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
	int re data=0;static char ch=0;ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))data=(data<<1)+(data<<3)+(ch^48),ch=getchar();
	return data;
}
typedef pair<int,int> T;T del,stk[N];
#define mp make_pair
inline void write(int x){if(x>9) write(x/10);putchar((x%10)^48);}
inline bool dfs_ring(int u){
	vis[u]=1;
	for(int re i=0;i^(int)e[u].size();++i){
		int re v=e[u][i]; 
		if(v==fa[u])continue;
		if(vis[v]){
			stk[++top]=mp(v,u);
			for(int re x=u;x!=v;x=fa[x])stk[++top]=mp(x,fa[x]);
			return 1;
		}fa[v]=u;if(dfs_ring(v))return 1;
	}return 0;
}
inline void dfs(int u,int ff,int k){
	path[k][++tot]=u;
	if(tot==n)return;
	for(int re i=0;i^(int)e[u].size();++i){
		int re v=e[u][i]; 
		if(v==ff)continue;
		if((u==del.first&&v==del.second)||(v==del.first&&u==del.second))continue;
		dfs(v,u,k);
	}
}
int main(){
	n=rd()+1,m=rd()+1;
	for(int re i=1;i^m;++i){
		int a=rd(),b=rd();
		e[a].push_back(b),e[b].push_back(a);
	}
	for(int re i=1;i^n;++i)sort(e[i].begin(),e[i].end());
	dfs_ring(1);
	if(m==n-1)dfs(1,0,1);
	else for(int re i=1;i<=top;++i){tot=0,del=stk[i],dfs(1,0,i);if(_ring(i))ans=i;}
	for(int re i=1;i^n;++i)write(path[ans][i]),putchar(' ');
	exit(0);
}
### 题目解析 P1645 序列问题描述为给定若干个区间,每个区间有对应的整数数量要求,需要在满足这些区间内整数数量要求的情况下,求出最少需要选择多少个整数。该问题可以使用差分约束系统来解决。差分约束系统是一种特殊的不等式组,通过将问题转化为图的最短路问题来求解。对于每个区间 `[x, y]` 要求至少有 `z` 个整数,可转化为不等式 `s[y] - s[x - 1] >= z`,其中 `s[i]` 表示从 `1` 到 `i` 选择的整数数量。同时,还有隐含条件 `0 <= s[i] - s[i - 1] <= 1`,表示每个位置最多选一个整数,最少不选。 ### 解题思路 1. **构建图**:将不等式转化为图的边。对于 `s[y] - s[x - 1] >= z`,可变形为 `s[x - 1] <= s[y] - z`,对应图中从 `y` 到 `x - 1` 有一条权值为 `-z` 的边;对于 `s[i] - s[i - 1] >= 0` 变形为 `s[i - 1] <= s[i]`,对应图中从 `i` 到 `i - 1` 有一条权值为 `0` 的边;对于 `s[i] - s[i - 1] <= 1` 变形为 `s[i] <= s[i - 1] + 1`,对应图中从 `i - 1` 到 `i` 有一条权值为 `1` 的边。 2. **求解最短路**:使用最短路算法(如 SPFA 算法)求解图的最短路。从一个源点开始,不断更新各点的最短距离。 3. **得出结果**:最终 `s[最大位置]` 即为满足所有区间要求的最少整数数量。 ### 代码实现 ```cpp #include <cstdio> #include <iostream> using namespace std; const int maxn = 100010; int head[maxn], nnext[maxn], to[maxn], team[maxn], length[maxn]; int n; int tot, s = 0, t = 0; int dis[maxn]; bool b[maxn]; void add(int x, int y, int l) { tot++; nnext[tot] = head[x]; head[x] = tot; to[tot] = y; length[tot] = l; } int main() { scanf("%d", &n); for (int i = 0; i <= 1000; i++) { add(i, i - 1, -1); add(i - 1, i, 0); } for (int i = 1; i <= n; i++) { int x, y, z; cin >> x >> y >> z; add(x - 1, y, z); } for (int i = 0; i <= 1000; i++) { dis[i] = -1e9; } dis[0] = 0; team[t] = 0; t++; b[0] = true; while (s != t) { int now = team[s]; s++; b[now] = false; for (int i = head[now]; i; i = nnext[i]) { int y = to[i]; if (dis[y] < dis[now] + length[i]) { dis[y] = dis[now] + length[i]; if (!b[y]) { team[t] = y; t++; b[y] = true; } } } } cout << dis[1000]; return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值