Trie:hdu 4825、1251、1247、Poj 3764


hdu 4825鏈接
在這裏插入圖片描述
題目意思很簡單,就是要求最大異或值的數。
我們可以從二進制的最高位開始選擇,不斷的排除一些數。我們先假設存在某些數字的二進制數是與當前查找的數不一樣的,我們進入這一部分數進行查找,以此重複,不斷排除一部分數,最後找到最佳的數。
如果在查找的過程中某一位不存在不相同的數,我們就只能從相同的數中查找了。
總結一句就是優先查找不同的。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef unsigned int ui;//unsigned int防止出現爆int.
const int N1 = 4e6 + 10;
ui trie[N1][2], tot, flag[N1];
void build_trie(ui x) {//建立字01字典樹的過程。
    int root = 0;
    for(int i = 31; i >= 0; i--) {題意不超過2^32,從32位開始,其實也可以30,但是不要超過31,如果用int.
        int u = x >> i & 1;
        if(!trie[root][u])    trie[root][u] = ++tot;
        root = trie[root][u];
    }
    flag[root] = x;//這個節點標記爲這個數的值。
}
ui find_max(ui x) {//找到與之匹配的異化值最大的數。
    int root = 0;
    for(int i = 31; i >= 0; i--) {
        int u = x >> i & 1;//取出這一位的二進制數。
        if(trie[root][!u])//優先進入與之不同的二進制數位。
            root = trie[root][!u];
        else    root = trie[root][u];
    }
    return flag[root];//返回查找的最優答案。
}
int main() {
//    freopen("D:\\Code\\ce.txt", "r", stdin);
    ui t, a, n, m;
    scanf("%u", &t);
    for(int k = 1; k <= t; k++) {
        printf("Case #%d:\n", k);
        tot = 0;
        memset(trie, 0, sizeof trie);//多組讀入,注意清零。
        scanf("%u %u", &n, &m);
        for(int i = 0; i < n; i++) {
            scanf("%u", &a);
            build_trie(a);
        }
        for(int i = 0; i < m; i++) {
            scanf("%u", &a);
            printf("%u\n", find_max(a));
        }
    }
    return 0;
}


hdu 1251鏈接
在這裏插入圖片描述
這題應該比上題還簡單,直接上代碼了。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 3e6 + 10;
int trie[N][26], flag[N], tot;
char s[20];
void build_trie() {
    int root = 0, n = strlen(s);
    for(int i = 0; i < n; i++) {
        int u = s[i] - 'a';
        if(!trie[root][u])    trie[root][u] = ++tot;
        root = trie[root][u];
        flag[root]++;//單詞經過的節點,加一。
    }
}
int find_sum() {
    int root = 0, n = strlen(s);
    for(int i = 0; i < n; i++) {
        int u = s[i] - 'a';
        if(!trie[root][u])    return 0;//出現一個字母在字典中不匹配,立即返回查找不到。
        root = trie[root][u];
    }
    return flag[root];//返回這個節點有多少單詞經過
}
int main() {
//    freopen("D:\\Code\\ce.txt", "r", stdin);
    while(gets(s)) {
        if(s[0] == 0)
            break;
        build_trie();
    }
    while(gets(s))
        printf("%d\n", find_sum());
    return 0;
}


hdu 1247鏈接
在這裏插入圖片描述
這題應該是比上兩題更難一點,具體看代碼詳解吧。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N1 = 5e6 + 10, N2 = 5e4 + 10;
int trie[N1][26], is_word[N1], tot;
string str[N2]; 
void build_trie(string s) {//建字典樹。
    int root = 0, n = s.size();
    for(int i = 0; i < n; i++) {
        int u = s[i] - 'a';
        if(!trie[root][u])    trie[root][u] = ++tot;
        root = trie[root][u];
    }
    is_word[root] = 1;//標記這個節點有單詞。
}
bool judge_tail(string s, int pos) {//判斷後綴是否是一個單詞,pos是後綴的起始位置。
    int root = 0, n = s.size();
    for(int i = pos; i < n; i++) {
        int u = s[i] - 'a';
        if(!trie[root][u])    return false;
        root = trie[root][u];
        if(is_word[root] && i == n - 1)//整個需要判斷的後綴是否是一個單詞。
            return true;
    }
    return false;
}
bool judge_front(string s) {//這裏判斷單詞的前綴是否是一個單詞。
    int root = 0, n = s.size();
    for(int i = 0; i < n; i++) {
        int u = s[i] - 'a';
        if(!trie[root][u])    return false;//有一個字母沒有查找到,返回失敗。
        root = trie[root][u];
        if(is_word[root] && judge_tail(s, i + 1))//前綴是一個單詞,接下來就是判斷後綴是否是一個單詞。
            return true;
    }
    return false;
}
int main() {
//    freopen("D:\\Code\\ce.txt", "r", stdin);
    int n = 0;
    while(cin >> str[n])
        build_trie(str[n++]);
    for(int i = 0; i < n; i++)
        if(judge_front(str[i]))
            cout << str[i] << endl; 
    return 0;
} 


Poj 3764鏈接
在這裏插入圖片描述
這題應該是這幾題裏面最難的題了,但是卻又跟第一題有點類似。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int N1 = 2e5 + 10, N2 = 4e6 + 10;
int trie[N2][2], tot, cnt, n, head[N1], a[N1];
struct Edge {//鏈式向前星。
	int to, next, value;
}edge[N1];
void add(int x, int y, int value) {//存圖的過程
	edge[cnt].to = y;
	edge[cnt].value = value;
	edge[cnt].next = head[x];
	head[x] = cnt++; 
}
void dfs(int u, int fa, int sum) {//通過dfs得到每個節點到根節點的異或值。
	a[u] = sum;
	for(int i = head[u]; i; i = edge[i].next) {
		if(edge[i].to != fa)	dfs(edge[i].to, u, sum ^ edge[i].value);
	}
}
void build_trie(int x) {//建立字典樹。
	int root = 0;
	for(int i = 30; i >= 0; i--) {
		int u = x >> i & 1;
		if(!trie[root][u])	trie[root][u] = ++tot;
		root = trie[root][u];
	}
}
int find_max(int x) {//查找最大值。
	int root = 0, ans = 0;
	for(int i = 30; i >= 0; i--) {
		int u = x >> i & 1;
		if(trie[root][!u]) {
			ans += (1 << i);//如果走這裏的話,我們可以得到答案的這一位是1,或者我們可以像第一題一樣現在建立字典樹的時候記錄下節點的值然後在返回這個最大值,最後再和當前查找的異或。
			root = trie[root][!u];
		}
		else	root = trie[root][u];
	}
	return ans;
}
int main() {
	while(scanf("%d", &n) != EOF) {
		tot = 0, cnt = 1;
		memset(trie, 0, sizeof trie);
		memset(head, 0, sizeof head); //多組輸入, 清零。
		for(int i = 1; i < n; i++) {
			int x, y, v;
			scanf("%d %d %d", &x, &y, &v);
			add(x, y, v);
			add(y, x, v);
		}
		dfs(0, 0, 0);
		for(int i = 0; i < n; i++)//得到異或值後,開始建字典樹。
			build_trie(a[i]);
		int ans = 0;
		for(int i = 0; i < n; i++)
			ans = max(ans, find_max(a[i]));
		printf("%d\n", ans);	
	}
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章