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;
}