• 2018NOIP提高组Day2(旅行(Tarjan)&填数游戏&保卫王国)



    TIPS:数据包下载(NOI官网)

    更新中……

    P5022 旅行(Tarjan)

    题目传送门:

    普通版
    数据加强

    思路

    怎么说这道题呢?看起来挺简单,但是细节问题很多。
    先说说m=n-1的情况:就是把每个点的子节点按从小到大编号排序然后从1号点开始遍历即可,60分到手
    麻烦的是m=n的情况:就是一棵基环树,大部分题解给的都是O(n2)的暴力枚举。由于我不大熟悉基环树,第一个想到的是Tarjan,结果92分(WA了两个点)搜题解都几乎没见过Tarjan的,但是以为用Tarjan的做法很简单就“独行其道”了,结果不知道掉了多少头发
    说正事:
    图就是一棵树再加上一条边,也就是有且只有一个环,普通的点很简单,按照m=n-1的方法即可,问题是和环有关的点,看图:
    在这里插入图片描述对于这个环,我们将第一个被访问的结点称为in结点,将环上已经被访问的结点标记为红色,它们的子结点标记为绿色,in的子结点(已排序)中(第一个 环上的点的)下一个点(有点绕,前面括号是为了断句)记为y点
    对于环上将要被访问的点,有几种选择:

    1. 访问
    2. 不访问,回溯到in点,并访问y点(条件:所有的绿点都己经被访问(否则绿点将无法到达))
    3. 不访问,回溯到红点,并访问该红点的未被访问的子结点(条件:沿路上所有红点的子结点均被访问(原因同上))

    上面几种选择,当然是要先满足条件再看哪一个最优,判断:
    变量说明x:当前结点(未访问),y:见上,z:从x点回溯到in的路上第一个未访问的绿点(其实这里表达不大好,绿点并不在该路径上,而是通过红点连接到环的旁边)
    当z-1时,z不存在,当y(1<<29)时,表示曾经已经回溯到y点,当普通树处理即可

    	if(tar[x] != dfn[x] && y != (1 << 29)){
    		if(z != -1){
    			if(x > z)return;
    		}
    		else if(y != -1){
    			if(x > y)return;
    		}
    	}
    

    那么如何判断一个点在不在环上呢?这就需要tarjan了,不会的先自学,dfs序存储在dfn数组里,tar[]就是“追溯值”。当dfn[i]!=tar[i]时,i点在环上,特别地,dfn[in]==tar[in]这就需要我们给in点一些特殊待遇了。

    代码

    92分

    其实比赛能拿到92已经很满意了

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <algorithm>
    #include <vector>
    #include <cstring>
    #define nn 5010
    using namespace std;
    int read(){
    	int re = 0 , sig = 1;
    	char c;
    	do
    		if((c = getchar()) == '-')sig = -1;
    	while(c < '0' || c > '9');
    	while(c >= '0' && c <= '9'){
    		re = (re << 1) + (re << 3) + c - '0';
    		c = getchar();
    	}
    	return re;
    }
    
    int n , m;
    bool vis[nn];
    vector <int> son[nn];
    
    
    void dfs(int x){
    	vis[x] = true;
    	printf("%d " , x);
    	for(int i = 0 ; i < son[x].size() ; i++)
    		if(!vis[son[x][i]])
    			dfs(son[x][i]);
    }
    int dfn[nn] , tar[nn];
    void tarjan(int x , int dep , int fa){
    	if(dfn[x] != 0)
    		return;
    	tar[x] = dfn[x] = dep;
    	for(int i = 0 ; i < son[x].size() ; i++){
    		tarjan(son[x][i] , dep + 1 , x);
    		if(tar[son[x][i]] < tar[x] && son[x][i] != fa)
    			tar[x] = tar[son[x][i]];
    	}
    }
    int y = -1;
    void dfs2(int x) {
    	if(x == y)
    		y = (1 << 29);
    //	cout << x << '	' << y << endl;
    	if(tar[x] != dfn[x]){
    		if(y < x){
    			return;
    		}
    	}
    	
    	vis[x] = true;
    	printf("%d " , x);
    	if(y == -1){
    		for(int i = 0 ; i < son[x].size() ; i++){
    			if(tar[son[x][i]] != dfn[son[x][i]])
    				y = son[x][i];
    		}
    	}
    		
    		
    	for(int i = 0 ; i < son[x].size() ; i++)
    		if(!vis[son[x][i]])
    			dfs2(son[x][i]);
    }
    int main(){
    	n = read();	m = read();
    	for(int i = 1 ; i <= m ; i++){
    		int u , v;
    		u = read() , v = read();
    		son[u].push_back(v);
    		son[v].push_back(u);
    	}
    	for(int i = 1 ; i <= n ; i++)
    		sort(son[i].begin() , son[i].end());
    	if(m == n - 1)
    		dfs(1);
    	else{
    		tarjan(1 , 1 , 0);
    //		for(int i = 1 ; i <= n ; i++)
    //			cout << tar[i] << '	';
    //		cout << endl;
    		memset(vis , 0 , sizeof(vis));
    		dfs2(1);
    	}
    	return 0;
    } 
    

    100分

    为了这8分掉了多少头发QAQ
    说明:以下做法效率应该为O(n),改一下数组大小依然可以通过洛谷数据加强版

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <algorithm>
    #include <vector>
    #include <cstring>
    #define nn 5010
    using namespace std;
    int read(){
    	int re = 0 , sig = 1;
    	char c;
    	do
    		if((c = getchar()) == '-')sig = -1;
    	while(c < '0' || c > '9');
    	while(c >= '0' && c <= '9'){
    		re = (re << 1) + (re << 3) + c - '0';
    		c = getchar();
    	}
    	return re;
    }
    
    int n , m;
    bool vis[nn];
    int in;
    vector <int> son[nn];
    
    void dfs(int x){
    	vis[x] = true;
    	printf("%d " , x);
    	for(int i = 0 ; i < son[x].size() ; i++)
    		if(!vis[son[x][i]])
    			dfs(son[x][i]);
    }
    int dfn[nn] , tar[nn];
    int dep = 1;
    void tarjan(int x , int fa){
    	if(dfn[x] != 0)
    		return;
    	tar[x] = dfn[x] = dep++;
    	for(int i = 0 ; i < son[x].size() ; i++){
    		tarjan(son[x][i] , x);
    		if(tar[son[x][i]] < tar[x] && son[x][i] != fa){
    			if(tar[son[x][i]] == dfn[son[x][i]])
    				in = son[x][i];
    			tar[x] = tar[son[x][i]];
    		}
    	}
    }
    int y = -1;
    void dfs2(int x , int z) {
    	if(tar[x] != dfn[x] && y != (1 << 29)){
    		if(z != -1){
    			if(x > z)return;
    		}
    		else if(y != -1){
    			if(x > y)return;
    		}
    	}
    	if(x == y)
    		y = (1 << 29);
    //	cout << x << '	' << y << '	' << z <<endl;
    	
    	vis[x] = true;
    	printf("%d " , x);
    
    	if(y == -1){
    		for(int i = 0 ; i < son[x].size() ; i++){
    			if(tar[son[x][i]] != dfn[son[x][i]]){
    				int j = i + 1;
    				while(vis[son[x][j]])j++;
    				y = son[x][j];
    				break;
    			} 
    		}
    	}//*/
    	for(int i = 0 ; i < son[x].size() ; i++)
    		if(!vis[son[x][i]]){
    			if(dfn[son[x][i]] != tar[son[x][i]] && y != (1 << 29) && x != in){
    				int j = i + 1;
    				while(j < son[x].size() && (vis[son[x][j]] || dfn[son[x][j]] != tar[son[x][j]] || in == son[x][j])){
    					j++;
    					if(j >= son[x].size())break;
    				}
    				if(j < son[x].size())
    					dfs2(son[x][i] , son[x][j]);
    				else dfs2(son[x][i] , z);
    			}
    			else dfs2(son[x][i] , z);
    		}
    }
    int main(){
    	freopen("input.txt" , "r" , stdin);
    	freopen("output.txt" , "w" , stdout);
    	n = read();	m = read();
    	for(int i = 1 ; i <= m ; i++){
    		int u , v;
    		u = read() , v = read();
    		son[u].push_back(v);
    		son[v].push_back(u);
    	}
    	for(int i = 1 ; i <= n ; i++)
    		sort(son[i].begin() , son[i].end());
    	if(m == n - 1)
    		dfs(1);
    	else{
    		tarjan(1 , 0);
    /*		for(int i = 1 ; i <= n ; i++)
    			cout << tar[i] << '	';
    		cout << endl;
    */		memset(vis , 0 , sizeof(vis));
    		dfs2(1 , -1);
    	}
    //	cout << in << endl;
    	return 0;
    } 
    

    对拍文件

    命名:
    数据生成:random.cpp
    对比:compare.cpp
    待测程序(见上):#4.cpp
    std程序:std.cpp

    数据生成(针对m==n情况,修改下也可生成树)

    #include <bits/stdc++.h>
    using namespace std;
    int random(int r , int l = 1){//范围[l,r]l默认为1
    	if(r == l)return l;
    	return (long long)rand() * rand() % (r - l) + l;
    }
    pair<int , int>e[5010];
    map<pair<int , int>,bool> h;
    
    int dict[5010];
    int main(){
    	freopen("input.txt" , "w" , stdout);
    	srand((unsigned)time(0));
    	
    	int n , m;
    	n = m = 5000;//可以自调范围
    	for(int i = 1 ; i <= n ; i++)
    		dict[i] = i;
    	random_shuffle(dict + 1 , dict + n + 1);
    	
    	for(int i = 2 ; i <= n ; i++){
    		int fa = random(i - 1);
    		e[i] = make_pair(fa , i);
    		h[e[i]] = h[make_pair(i , fa)] = 1;
    	}
    	int x , y;
    	do{
    		x = random(n) , y = random(n);
    	}
    	while(x == y || h[make_pair(x , y)]);
    	e[1] = make_pair(x , y);
    	
    	random_shuffle(e + 1 , e + m + 1);
    	printf("%d %d
    " , n , m);
    	for(int i = 1 ; i <= n ; i++){
    		printf("%d %d
    " , dict[e[i].first] , dict[e[i].second]);
    	}
    	return 0;
    }
    /*
    	freopen("input.txt" , "r" , stdin);
    	freopen(".txt" , "w" , stdout);
    	
    */
    

    自动对比程序

    #include <bits/stdc++.h>
    #include <windows.h>
    using namespace std;
    int main(){
    	while(true){
    		system("start random.exe");
    		Sleep(50);
    		system("start #4.exe");
    		system("start std.exe");
    		Sleep(1500);
    		if(system("fc output.txt ans.txt")){
    			system("start input.txt");
    			cout << "WA!!!" << endl;
    			return 0;
    		}
    	}
    	
    	return 0;
    }
    

    std程序(来自NOI官网)

    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    
    const int MAXN = 300005;
    
    int n, m;
    
    struct data {
    	int u, v, id;
    };
    
    bool operator < (const data &a, const data &b) {
    	return a.u < b.u || (a.u == b.u && a.v < b.v);
    }
    
    data _edges[MAXN * 2];
    data *first[MAXN];
    
    int ans[MAXN];
    int dfsseq[MAXN];
    int visit[MAXN];
    
    int del_e;
    int dfs_idx;
    bool ok;
    bool fail;
    
    #define visit_id (del_e + 1)
    
    void dfs(int x) {
    	if (!ok) {
    		if (x < ans[dfs_idx]) {
    			ok = true;
    		} else if (x > ans[dfs_idx]) {
    			fail = true;
    			return;
    		}
    	}
    	dfsseq[dfs_idx++] = x;
    	visit[x] = visit_id;
    	for (data *d = first[x]; d->u == x; d++) if (d->id != del_e) {
    		int y = d->v;
    		if (visit[y] == visit_id) continue;
    		dfs(y);
    		if (fail) return;
    	}
    }
    
    
    
    int main() {
    	freopen("input.txt" , "r" , stdin);
    	freopen("ans.txt" , "w" , stdout);
    	scanf("%d%d", &n, &m);
    	if (n == 1) {
    		printf("1
    ");
    		return 0;
    	}
    	for (int i = 0; i < m; i++) {
    		int u, v;
    		scanf("%d%d", &u, &v);
    		_edges[i * 2] = (data){u, v, i};
    		_edges[i * 2 + 1] = (data){v, u, i};
    	}
    	std::sort(_edges, _edges + 2 * m);
    	first[1] = _edges;
    	for (int i = 1; i < 2 * m; i++) {
    		if (_edges[i].u != _edges[i - 1].u) {
    			first[_edges[i].u] = _edges + i;
    		}
    	}
    	ans[0] = 2;
    	if (n == m) {
    		for (int i = 0; i < m; i++) {
    			del_e = i;
    			dfs_idx = 0;
    			ok = false;
    			fail = false;
    			dfs(1);
    			if (dfs_idx == n && ok && !fail) {
    				memcpy(ans, dfsseq, n * sizeof(int));
    			}
    		}
    	} else {
    		del_e = -2;
    		dfs(1);
    		memcpy(ans, dfsseq, n * sizeof(int));
    	}
    	for (int i = 0; i < n; i++) {
    		printf("%d ", ans[i]);
    	}
    }
    

    P5023 填数游戏

    P5024 保卫王国

    题目传送门

    https://www.luogu.com.cn/problem/P5024

    44分思路

    经典的树形DP,我们考虑对于国王的每一个问题跑一遍DP,时间复杂度O(nm)44分
    做法:设f[i][0/1]表示以i为根的树的最小费用,0表示i结点不驻兵,1则为驻兵
    当i不驻兵时,它的子结点必须驻兵因此

    [f[i][0]=Sigma f[j][1](j为i的子结点) ]

    特别地,当j被强制要求不能驻兵时,f[i][0]不存在,赋值为无穷大
    当i驻兵时,i的子结点可驻兵或不驻兵

    [f[i][1]=Sigma min{^{f[j][0]}_{f[j][1]}(j为i的子结点) ]

    关于国王强制要求的处理:

    		a = read();
    		if((x = read()) == 0)	f[a][1] = inff;
    		else					f[a][0] = inff;
    		b = read();
    		if((y = read()) == 0)	f[b][1] = inff;
    		else					f[b][0] = inff;
    

    44分代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define inff (1 << 29)
    #define nn 100010
    using namespace std;
    int read(){
    	int re = 0 , sig = 1;
    	char c = getchar();
    	while(c < '0' || c > '9'){
    		if(c == '-')sig = -1;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9'){
    		re = (re << 1) + (re << 3) + c - '0';
    		c = getchar();
    	}
    	return re * sig;
    }
    struct node{
    	int u , nxt;
    }ed[nn];
    int head[nn];
    void addedge(int u , int v){
    	static int top = 1;
    	ed[top].u = v , ed[top].nxt = head[u] , head[u] = top;
    	top++;
    	ed[top].u = u , ed[top].nxt = head[v] , head[v] = top;
    	top++;
    }
    inline int minn(int a , int b){
    	return a < b ? a : b;
    }
    int n , m;
    int p[nn];
    
    bool vis[nn];
    int f[nn][2];
    
    void dfs(int x){
    	vis[x] = true;
    	for(int i = head[x] ; i ; i = ed[i].nxt)
    		if(!vis[ed[i].u])
    			dfs(ed[i].u);
    	if(f[x][0] != inff){
    		f[x][0] = 0;
    		for(int i = head[x] ; i ; i = ed[i].nxt)
    			if(!vis[ed[i].u]){
    				if(f[ed[i].u][1] == inff){
    					f[x][0] = inff;
    					break;
    				}
    				f[x][0] += f[ed[i].u][1];
    			}
    	}
    	if(f[x][1] != inff){
    		f[x][1] = p[x];
    		for(int i = head[x] ; i ; i = ed[i].nxt)
    			if(!vis[ed[i].u])
    				f[x][1] += minn(f[ed[i].u][0] , f[ed[i].u][1]);
    	}
    	vis[x] = false;
    }
    signed main(){
    	n = read() , m = read() , read();
    	for(int i = 1 ; i <= n ; i++)
    		p[i] = read();
    	for(int i = 1 ; i < n ; i++)
    		addedge(read() , read());
    	for(int i = 1 ; i <= m ; i++){
    		memset(f , 0 , sizeof(f));
    		int a , b , x , y;
    		a = read();
    		if((x = read()) == 0)	f[a][1] = inff;
    		else					f[a][0] = inff;
    		b = read();
    		if((y = read()) == 0)	f[b][1] = inff;
    		else					f[b][0] = inff;
    		
    		if(x == 0 && y == 0){
    			int j;
    			for(j = head[a] ; j ; j = ed[j].nxt)
    				if(ed[j].u == b){
    					printf("-1
    ");
    					break;
    				}
    			if(j)continue;
    		}
    			
    		dfs(1);
    		printf("%d
    " , minn(f[1][0] , f[1][1]));
    	}
    	return 0;
    }
    

    未完……

  • 相关阅读:
    js的红黑树
    javaScript实现平衡树
    使用javaScript实现一个二叉树,实现插入节点,删除节点,查询节点,最大最小值查询,中序,前序,后序遍历功能
    使用javaScript实现散列表的线性探查法
    使用javaScript实现处理散列表的冲突的方法之分离链接
    使用javaScript实现一个字典
    使用javaScript实现集合
    使用javaScript实现一个堆链表
    使用javaScript实现一个双端链表
    kmp
  • 原文地址:https://www.cnblogs.com/dream1024/p/13957596.html
Copyright © 2020-2023  润新知