zoukankan      html  css  js  c++  java
  • JZOJ 6085. 【GDOI2019模拟2019.3.26】要换换名字(二分+Trie+二分图匹配)

    JZOJ 6085. 【GDOI2019模拟2019.3.26】要换换名字

    题目大意

    • 给出 n n n个由小写字母组成的字符串,每个串用它的某个非空子序列替代它,求使得替代后所有串互不相同的最长串最小长度。若不存在则输出 − 1 -1 1
    • 1 ≤ n , l e n ≤ 300 1le n,lenle300 1n,len300

    题解

    • 先二分答案,给每个串找出长度小于 m i d mid mid n n n个子序列,如果不足 n n n个则找出所有子序列。任意找 n n n个即可,因为只要有 n n n个就能使得不出现重复。
    • 既然已经找出来了每个串替换为什么子序列,那么把它们连边,跑一次二分图匹配,若能得到最大匹配则符合条件,否则不符合。
    • 要注意在这里不同串找出的子序列可能会相同,可以放在Trie上去重。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 310
    #define ll long long
    char a[N][N];
    int ch[N * N], vi[N * N];
    int ls[N], f[N][N][26];
    int last[N], nxt[N * N], to[N * N], len;
    int last1[N * N * 2], nxt1[N * N], to1[N * N], len1;
    int n, tot, sum, tr = 1;
    struct node {
    	int a[N];
    }st[N * N], s0[N], c;
    struct {
    	int p[26];
    }T[N * N * 2];
    void add1(int x, int y) {
    	to1[++len1] = y;
    	nxt1[len1] = last1[x];
    	last1[x] = len1;
    }
    void add(int x, int y) {
    	to[++len] = y;
    	nxt[len] = last[x];
    	last[x] = len;
    }
    void dfs(int k, int x, int s, int ln, int o) {
    	if(tot == n) return;
    	if(s > 0) tot++, add1(o, k);
    	if(s < ln) {
    		for(int i = 0; i < 26 && tot < n; i++) if(f[k][x][i] > 0) {
    			int o0;
    			if(T[o].p[i]) o0 = T[o].p[i]; else o0 = T[o].p[i] = ++tr;
    			dfs(k, f[k][x][i], s + 1, ln, o0);
    		}	
    	}
    }
    void goes(int k) {
    	if(last1[k]) {
    		st[++sum] = c;
    		for(int i = last1[k]; i; i = nxt1[i]) add(to1[i], sum);
    	}
    	for(int i = 0; i < 26; i++) if(T[k].p[i]) {
    		c.a[++c.a[0]] = i;
    		goes(T[k].p[i]);
    		c.a[0]--;
    	}
    }
    int solve(int k, int id) {
    	for(int i = last[k]; i; i = nxt[i]) if(vi[to[i]] < id) {
    		vi[to[i]] = id;
    		if(!ch[to[i]] || solve(ch[to[i]], id)) {
    			ch[to[i]] = k;
    			return 1;
    		}
    	}
    	return 0;
    }
    int main() {
    	int i, j, k;
    	scanf("%d", &n);
    	memset(f, 255, sizeof(f));
    	for(i = 1; i <= n; i++) {
    		scanf("%s", a[i] + 1);
    		ls[i] = strlen(a[i] + 1);
    		for(j = ls[i] - 1; j >= 0; j--) {
    			for(k = 0; k < 26; k++) f[i][j][k] = f[i][j + 1][k];
    			f[i][j][a[i][j + 1] - 'a'] = j + 1;
    		}
    	}
    	int l = 1, r = 300, ans = -1;
    	while(l <= r) {
    		int mid = (l + r) / 2;
    		memset(last, 0, sizeof(last));
    		memset(last1, 0, sizeof(last1));
    		len1 = len = sum = 0;
    		for(i = 1; i <= n; i++) {
    			tot = 0;
    			dfs(i, 0, 0, mid, 1);
    		}
    		goes(1);
    		memset(ch, 0, sizeof(ch));
    		memset(vi, 0, sizeof(vi));
    		int mat = 0;
    		for(i = 1; i <= n; i++) if(solve(i, i)) mat++;
    		if(mat == n) {
    			r = mid - 1;
    			ans = mid;
    			for(i = 1; i <= sum; i++) if(ch[i]) s0[ch[i]] = st[i];
    		}
    		else l = mid + 1;
    	}
    	printf("%d
    ", ans);
    	if(ans > 0) {
    		for(i = 1; i <= n; i++) {
    			for(j = 1; j <= s0[i].a[0]; j++) printf("%c", 'a' + s0[i].a[j]);
    			puts("");
    		}
    	}
    	return 0;
    }
    

    自我小结

    • 这题一开始并没有任何像正解的思路,所以只写了随机化和贪心,但只能过小数据和纯随机的数据。
    • 有想到过二分,但不知道如何判断可行。只要想到“只需搜出 n n n个子序列”,便能容易想到接下来的做法。
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    Java应用程序的运行机制,以及JDK,JRE,JVM
    正则表达式练习题
    正则表达式
    转:DOM操作
    jQuery 遍历
    转:把一个对象赋值给另一个对象会指向同一个内存地址
    sql 语句的先后执行顺序
    数据结构时间复杂度进阶篇
    数据结构时间复杂度基础篇
    UML图中类之间的关系:依赖,泛化,关联,聚合,组合,实现(转)
  • 原文地址:https://www.cnblogs.com/LZA119/p/14608424.html
Copyright © 2011-2022 走看看