hdu - 4324 - Triangle LOVE - 深搜

2000个点的有向图,保证任意两点间仅有一条有向边,问是否存在一个三元环。

http://acm.hdu.edu.cn/showproblem.php?pid=4324

解法一:

     

如果把任意有公共顶点的两边构成的图形看做一个角,可将角分为是b的和不是b的(用a类表示),把任意三条边看做一个三角形,可将三角形分为x、y两种。对于每个顶点入度为in出度为out则共有in*out种b种角,由于是竞赛图,因此有C(n,2)-in*out种a种角,根据3x+y=b,2y=a,因此x=(b-a/2)/3,即可求出三元环的个数。

解法二:

   归结为1个条件,任何两人都有边,要么出要么入。
对于有向三元环,我们知道找到:A->B->C->A ,B->C->A->B ,C->A->B->C 是一样的,这给0(n^2)的算法提供了基础。
对于每次枚举i,我们在(0~i-1)的范围看下有多少个i指向的点(剩下的就是指向i的点),同时算下i指向的点的出度和。就可以知道这些 i指向的点 指向 指向i的点(剩下的点)的数目,如果
              num * (num - 1) / 2 < sumout

 那么就是被指向的点又指回去了,这样就形成了3元环。

否则,剩下的就是更新下出度即可,继续执行下一个节点。

解法三:

     结论:对于一个竞赛图,若存在n元环,一定存在一个n-1元环或者三元环,此题可以一遍拓扑排序判环求解即只需要找到一个环,就必定存在三元环。证明如下: 假设存在一个n元环,因为a->b有边,b->a必定没边,反之也成立 所以假设有环上三个相邻的点a-> b-> c,那么如果c->a间有边,就已经形成了一个三元环,如果c->a没边,那么a->c肯定有边,这样就形成了一个n-1元环。。。。 所以只需证明n为4时一定有三元环即可,显然成立。

解法四:

   增量算法,充分利用“任意两点间仅有一条有向边”的性质。
假设前面已经加入了N个点,现在来了第N+1个点。
那么一定能将N个点分成left和right两部分,使得N+1号点到left有边,right到N+1号点右边(因为任意两点间都有边),那么,如果left的任意一个点l到right任意一个点r有边的话,那么就有答案N+1->l->r->N+1这样一个长度为3的环。
那么每次加入N+1号点后,用O(N)的复杂度求出左边的数量leftnum,右边的数量rightnum,left的出度和leftout,left的入度和leftin。
如果left没有一条到right的边,则一定满足:
leftin = leftout + leftnum * rightnum(left和right任意两点右边,如果没有左到右的,那么leftnum*rightnum条边都是右到左的)
那么,如果leftin != leftout + leftnum * rightnum,则暴力枚举左点,右点即可得到答案。
总体复杂度O(n^2)

解法五:

     直接深搜。

1:

int main(){
    scanf("%d",&T);
    for(int ca = 1; ca <= T; ca ++){
        scanf("%d",&n);
        memset(in, 0 , sizeof(in));
        memset(out, 0, sizeof(out));
        for(int i = 1; i <= n; i ++){
            scanf("%s",tmp + 1);
            for(int j = 1; j <= n; j ++){
                if(tmp[j] == '1'){
                    in[j] ++;
                    out[i] ++;
                }
            }
        }
        long long b, a, ans, all ;
        b = all = 0;
        for(int i = 1; i <= n; i ++){
            b += in[i] * out[i];
            all += (n - 1) * (n - 2) / 2;
        }
//        all += ( (n - 1) / 2 * (n - 2) )* n; 这么写就错了,加在里面就对,无语了。
//        printf("all af = %d\n", all);
         a = all - b;
         ans = ( b - a / 2 ) / 3;

//        printf("%dkaka\n", ans);

        printf("Case #%d: ",ca);
        if(ans) printf("Yes\n");
        else puts("No");
    }
    return 0;
}
2:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

const int maxn = 2012;
char s[maxn][maxn];
int InDegree[maxn];

int main(){
    int T;
    scanf("%d", &T);
    for(int t=1; t<=T; t++){

       int n;
       scanf("%d ", &n);
       for(int i=0; i<n; i++)
           gets(s[i]);
       for(int i=0; i<n; i++){
           InDegree[i] = 0;
           for(int j=0; j<n; j++){
              if(s[i][j] == '0' && i!=j)
                  InDegree[i]++;
           }
       }
       printf("Case #%d: ", t);
       for(int i=0; i<n; i++){
           int sum = 0, x = 0;
           for(int j=0; j<n; j++)
              if(s[j][i] == '1'){
                  sum += InDegree[j];//分两组,记录喜欢自己那组的入度和
                  x++;
              }
              //x为指向i的有几个, sum为指向i的那几个的入度和
           if(sum > x*(x-1)/2){
              printf("Yes\n");
              goto RL;
           }
       }
       printf("No\n");
       RL:continue;
    }
    return 0;
}


4:

#include <cstdio>
#include <cstring>
#define maxn 2010
using namespace std;
int T,n,edgenum, head[maxn * maxn];
char tmp[maxn];
bool vis[maxn], chu[maxn];
inline void initEdge(){
    memset(head, -1, sizeof(head));
    edgenum = 0;
}
struct Edge{
    int v, nxt;
}edge[maxn * maxn];
void addEdge(int u, int v){
    edge[edgenum].v = v;
    edge[edgenum].nxt = head[u];
    head[u] = edgenum ++;
}
bool record[maxn][maxn];
bool dfs(int u, int depth){
    if(vis[u]){
        if(depth > 2 && record[depth - 3][u])
            return true;
        return false;
    }else{
        vis[u] = true;
        record[depth][u] = true;
        for(int i = head[u]; i != -1; i = edge[i].nxt){
            if(dfs(edge[i].v, depth + 1))
                return true;
        }
    }
    return false;
}

int main(){
    scanf("%d",&T);
    for(int ca = 1; ca <= T; ca ++){
        scanf("%d",&n);
        initEdge();
        memset(chu, false, sizeof(chu));
        for(int i = 1; i <= n; i ++){
            scanf("%s",tmp + 1);
            for(int j = 1; j <= n; j ++){
                if(tmp[j] == '1'){
                    addEdge(i,j);
                    chu[i] = true;
                }
            }
        }
        memset(vis, false, sizeof(vis));
        memset(record, false, sizeof(record));
        bool flag = false;
        for(int i = 1; i <= n && !flag; i ++){
            if(!vis[i]&&chu[i]){
                flag = dfs(i, 0);
            }
        }
        printf("Case #%d: ",ca);
        if(flag) printf("Yes\n");
        else puts("No");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值