zoukankan      html  css  js  c++  java
  • @hdu


    @description@

    我们称一个有向图G是传递的,当且仅当对任意三个不同的顶点a,,若G中有一条边从a到b且有一条边从b到c ,则G中同样有一条边从a到c。
    现在,给你两个有向图P = (V,Ep)和Q = (V,Ee),满足 Ep与Ee没有公共边且 (V,Ep⋃Ee)是一个竞赛图(有向完全图)。
    你的任务是:判定是否P,Q同时为传递的。

    Input
    包含至多20组测试数据。
    第一行有一个正整数,表示数据的组数。
    对于每组数据,第一行有一个正整数n。接下来n行,每行为连续的n个字符,每 个字符只可能是’-’,’P’,’Q’中的一种。
    ∙如果第i行的第j个字符为’P’,表示有向图P中有一条边从i到j;
    ∙如果第i行的第j个字符为’Q’,表示有向图Q中有一条边从i到j;
    ∙否则表示两个图中均没有边从i到j。
    保证1 <= n <= 2016,一个测试点中的多组数据中的n的和不超过16000。保证输入的图一定满足给出的限制条件。

    Output
    对每个数据,你需要输出一行。如果P 与 Q都是传递的,那么请输出’T’。否则, 请输出’N’ (均不包括引号)。

    Sample Input
    4
    4
    -PPP
    --PQ
    ---Q
    ----
    4
    -P-P
    --PQ
    P--Q
    ----
    4
    -PPP
    --QQ
    ----
    --Q-
    4
    -PPP
    --PQ
    ----
    --Q-
    Sample Output
    T
    N
    T
    N

    @solution@

    感觉正解还是挺巧妙的,虽然我在网上看到的题解大多是用乱搞水过去的。。。

    考虑如果不传递,那么一定存在 a, b, c 使得 a->b, b->c 但不存在 a->c。
    不存在 a->c 有几种情况呢?一种是存在 c->a,一种是在另一个图中存在 a->c/c->a。

    假如存在 c->a,则形成一个三元环。所以其中一个不合法的判定是找图中是否有环。
    那如果 a, c 之间的边在另一边呢?假如另一边也是 c->a,则将两个图拼在一起后又形成了三元环。
    那么是否拼在一起后原本不会出环的地方变出环来呢?答案是不会。考虑竞赛图有一个性质:如果有环,则必然有三元环(这个性质还是很容易证的吧)。枚举一下三元环中边的所属情况,发现无论如何都会落入上面的不合法情况。

    所以,我们可以通过将图 P 和图 Q 拼起来找环,解决“存在 c->a”,“另一个图中存在 c->a” 这两种不合法情况。
    那么“另一个图中存在 a->c”怎么办呢?我们可以考虑将图 Q 的边反向过后在和图 P 拼起来再找环,一样可以证明这样是不会讲合法误判成不合法。

    至于找环 tarjan 啊拓扑排序啊都可以找的。

    @accepted code@

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXN = 2016;
    int G[MAXN + 5][MAXN + 5], n;
    int tid[MAXN + 5], low[MAXN + 5], dcnt, tot;
    int stk[MAXN + 5], tp; bool vis[MAXN + 5];
    char str[MAXN + 5];
    int func(char ch) {
    	if( ch == 'P' ) return 1;
    	else if( ch == 'Q' ) return -1;
    	else return 0;
    }
    void dfs(int x) {
    	tid[x] = low[x] = (++dcnt);
    	vis[x] = true; stk[++tp] = x;
    	for(int i=1;i<=n;i++) {
    		if( !G[x][i] ) continue;
    		if( !tid[i] )
    			dfs(i), low[x] = min(low[x], low[i]);
    		else if( vis[i] )
    			low[x] = min(low[x], tid[i]);
    	}
    	if( low[x] >= tid[x] ) {
    		tot++;
    		while( tp && tid[stk[tp]] >= tid[x] )
    			vis[stk[tp]] = false, tp--;
    	}
    }
    void solve() {
    	scanf("%d", &n);
    	for(int i=1;i<=n;i++) {
    		scanf("%s", str + 1);
    		for(int j=1;j<=n;j++)
    			G[i][j] = func(str[j]);
    	}
    	dcnt = tot = 0;
    	for(int i=1;i<=n;i++) tid[i] = 0;
    	for(int i=1;i<=n;i++)
    		if( !tid[i] ) dfs(i);
    	if( tot != n ) {
    		puts("N");
    		return ;
    	}
    	for(int i=1;i<=n;i++)
    		for(int j=i;j<=n;j++)
    			if( G[i][j] == -1 || G[j][i] == -1 )
    				swap(G[i][j], G[j][i]);
    	dcnt = tot = 0;
    	for(int i=1;i<=n;i++) tid[i] = 0;
    	for(int i=1;i<=n;i++)
    		if( !tid[i] ) dfs(i);
    	if( tot != n ) {
    		puts("N");
    		return ;
    	}
    	puts("T");
    }
    int main() {
    	int T; scanf("%d", &T);
    	for(int i=1;i<=T;i++) solve();
    }
    

    @details@

    感觉这么巧妙的正解(看起来有问题但实际上严格正确),为什么网上都是用乱搞过的。。。

    以及我为什么要用 tarjan 找环而不直接写个拓扑排序就好了。。。

  • 相关阅读:
    如何学习一项新技术呢?
    开发人员如何构建自己的学习笔记系统
    Stream流之List、Integer[]、int[]相互转化
    图解LinkedHashMap原理
    java 手动实现单链表(尾插法和头插法)
    为什么你学不会递归?刷题几个月,告别递归,谈谈我的经验
    谈谈限流算法的几种实现
    使用LinkedHashMap实现一个简易的LRU缓存
    Java深拷贝和浅拷贝
    Excel两列求差集和并集的实现
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11381602.html
Copyright © 2011-2022 走看看