zoj 4124 "Median" (思维?假的图论?)
来源:2019 年“浪潮杯”第十届山东省 ACM 省赛
题意:
对于一个包含n个数的(n为奇数)序列val[ ],排序后的 val[ (n+1) / 2 ] 定义为 median;
有 n 个数,并有 m 组关系,对于第 i 组关系 ai,bi 代表第 val[ai] > val[bi];
但并没有给出具体的数值;
输出一个包含 n 个元素的数组 s[ ] ;
让你判断第 i 个数 val[ i ]是可能为中位数,如果是,第 i 位为 1;
如果不是,第 i 位为 0;
输出 n 个数,其中第 i 个数为 0 或 1,含义如上;
题解:
首先,特判两种情况:
①ai = bi;
②给出的 m 个关系有环;
对于这两种情况,输出 n 个 0;
除了这两种情况外,就是一个有向无环图;
如何判断第 i 位是否为 median 呢?
搜索:
正向搜索比第 i 个数小的数的总个数 tot1;
反向搜索比第 i 个数大的数的总个数 tot2;
那么,还剩下 res = n-(tot1+tot2+1) 个数;
如果 res ≥ |tot1-tot2|,那么第 i 个数就是中位数;
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mem(a,b) memset(a,b,sizeof(a)) 4 const int maxn=1e4+50; 5 6 int n,m; 7 int num; 8 int head[maxn]; 9 bool vis[150]; 10 bool isCir; 11 char s[200]; 12 struct Edge 13 { 14 int to; 15 int next; 16 }G[maxn<<1]; 17 void addEdge(int u,int v) 18 { 19 G[num]={v,head[u]}; 20 head[u]=num++; 21 } 22 struct SCC 23 { 24 vector<int >vs; 25 void DFS(int u) 26 { 27 vis[u]=true; 28 for(int i=head[u];~i;i=G[i].next) 29 { 30 int v=G[i].to; 31 if((i&1) || vis[v]) 32 continue; 33 DFS(v); 34 } 35 vs.push_back(u); 36 } 37 void RDFS(int u,int k) 38 { 39 vis[u]=true; 40 for(int i=head[u];~i;i=G[i].next) 41 { 42 int v=G[i].to; 43 if(!(i&1) || vis[v]) 44 continue; 45 RDFS(v,k); 46 } 47 } 48 int scc() 49 { 50 vs.clear(); 51 mem(vis,false); 52 for(int i=1;i <= n;++i) 53 if(!vis[i]) 54 DFS(i); 55 56 int k=0; 57 mem(vis,false); 58 for(int i=vs.size()-1;i >= 0;--i) 59 if(!vis[vs[i]]) 60 RDFS(vs[i],++k); 61 return k; 62 } 63 }_scc; 64 int DFS(int u)///正向搜索比第 i 个数小的数 65 { 66 int ans=1; 67 vis[u]=true; 68 for(int i=head[u];~i;i=G[i].next) 69 { 70 int v=G[i].to; 71 if((i&1) || vis[v]) 72 continue; 73 ans += DFS(v); 74 } 75 return ans; 76 } 77 int RDFS(int u)///反向搜索比第 i 个数大的数 78 { 79 int ans=1; 80 vis[u]=true; 81 for(int i=head[u];~i;i=G[i].next) 82 { 83 int v=G[i].to; 84 if(!(i&1) || vis[v]) 85 continue; 86 ans += RDFS(v); 87 } 88 return ans; 89 } 90 bool isSat(int u) 91 { 92 mem(vis,false); 93 int tot1=DFS(u)-1; 94 mem(vis,false); 95 int tot2=RDFS(u)-1; 96 97 return abs(tot1-tot2) <= (n-tot1-tot2-1) ? true:false; 98 } 99 char *Solve() 100 { 101 mem(s,'0'); 102 s[n]='\0'; 103 int k=_scc.scc();///强连通分量分解判断是否含有环 104 if(k < n) 105 isCir=true; 106 if(isCir) 107 return s; 108 109 for(int i=1;i <= n;++i) 110 if(isSat(i))///判断第 i 个数是否为median 111 s[i-1]='1'; 112 return s; 113 } 114 void Init() 115 { 116 num=0; 117 mem(head,-1); 118 isCir=false; 119 } 120 int main() 121 { 122 // freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin); 123 int test; 124 scanf("%d",&test); 125 while(test--) 126 { 127 scanf("%d%d",&n,&m); 128 Init(); 129 for(int i=1;i <= m;++i) 130 { 131 int u,v; 132 scanf("%d%d",&u,&v); 133 if(u == v) 134 isCir=true; 135 addEdge(u,v);///正向边 136 addEdge(v,u);///反向边 137 } 138 printf("%s\n",Solve()); 139 } 140 return 0; 141 }
凯少思路:
如果 tot1 ≤ n/2 && tot2 ≤ n/2 返回 true;
在返回结果的时候改成这句也可以,我的判断方法也对;
代码:
1 #include <iostream> 2 #include <vector> 3 #include <cstring> 4 #include <queue> 5 #include <cstdio> 6 #include <algorithm> 7 #include <cmath> 8 #include <stack> 9 using namespace std; 10 const int ss = 200; 11 int v1[ss]; 12 int v2[ss]; 13 int dfn[ss]; 14 int low[ss]; 15 int vis[ss]; 16 int son1[ss]; 17 int son2[ss]; 18 int pot[ss]; 19 vector < int >G[ss]; 20 vector < int >P[ss]; 21 stack < int >S; 22 int scc, tim, n, m, t; 23 void start() 24 { 25 memset(v1, 0, sizeof(v1)); 26 memset(v2, 0, sizeof(v2)); 27 memset(dfn, 0, sizeof(dfn)); 28 memset(vis, 0, sizeof(vis)); 29 memset(low, 0, sizeof(low)); 30 memset(pot, 0, sizeof(pot)); 31 memset(son1, 0, sizeof(son1)); 32 memset(son2, 0, sizeof(son2)); 33 for (int i = 1; i <= n; i++) 34 { 35 G[i].clear(); 36 P[i].clear(); 37 } 38 while (!S.empty()) 39 S.pop(); 40 scc = tim = n = m = 0; 41 } 42 void tar(int u) 43 { 44 int v; 45 low[u] = dfn[u] = ++tim; 46 S.push(u); 47 vis[u] = 1; 48 for (int i = 0; i < G[u].size(); i++) 49 { 50 v = G[u][i]; 51 if (!dfn[v]) 52 { 53 tar(v); 54 if (low[u] > low[v]) 55 low[u] = low[v]; 56 } else if (vis[v] && low[u] > dfn[v]) 57 low[u] = dfn[v]; 58 } 59 if (low[u] == dfn[u]) 60 { 61 scc++; 62 do 63 { 64 v = S.top(); 65 S.pop(); 66 vis[v] = 0; 67 } while (v != u); 68 } 69 } 70 int dfs1(int x) 71 { 72 v1[x] = 1; 73 for (int i = 0; i < G[x].size(); i++) 74 { 75 if (!v1[G[x][i]]) 76 son1[x] += dfs1(G[x][i]); 77 } 78 return son1[x] + 1; 79 } 80 int dfs2(int x) 81 { 82 v2[x] = 1; 83 for (int i = 0; i < P[x].size(); i++) 84 { 85 if (!v2[P[x][i]]) 86 son2[x] += dfs2(P[x][i]); 87 } 88 return son2[x] + 1; 89 } 90 void print(int x, int p) 91 { 92 for (int i = 1; i <= n; i++) 93 { 94 if (i == p) 95 printf("1"); 96 else 97 printf("0"); 98 } 99 printf("\n"); 100 } 101 int main() 102 { 103 cin >> t; 104 while (t--) 105 { 106 start(); 107 int ok = 1; 108 cin >> n >> m; 109 for (int i = 1; i <= m; i++) 110 { 111 int a, b; 112 scanf("%d%d", &a, &b); 113 if (a == b) 114 ok = 0; 115 G[a].push_back(b); 116 P[b].push_back(a); 117 } 118 if (!ok) 119 { 120 print(n, 0); 121 } else 122 { 123 for (int i = 1; i <= n; i++) 124 if (!dfn[i]) 125 tar(i); 126 if (scc != n) 127 print(n, 0); 128 else 129 { 130 int point = 0; 131 for (int i = 1; i <= n; i++) 132 { 133 dfs1(i); 134 dfs2(i); 135 if ((son1[i] <= (n / 2)) && (son2[i] <= (n / 2))) 136 pot[i] = 1; 137 memset(son1, 0, sizeof(son1)); 138 memset(son2, 0, sizeof(son2)); 139 memset(v1, 0, sizeof(v1)); 140 memset(v2, 0, sizeof(v2)); 141 } 142 for (int i = 1; i <= n; i++) 143 cout << pot[i]; 144 cout << "\n"; 145 } 146 } 147 } 148 return 0; 149 }
因为 n 很小,所以对每个点跑两次DFS并不会超时;
但,如果 n 大了呢,那制定不能每个点跑两次DFS了,那该肿么办呢?
本蒟蒻还没想出来~~~~~~~~
据说,此题正解为求解两次拓扑序;