来源: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了,那该肿么办呢?
本蒟蒻还没想出来~~~~~~~~
据说,此题正解为求解两次拓扑序;
https://paste.ubuntu.com/p/xXxYdnDRBV/
来源:oschina
链接:https://my.oschina.net/u/4305937/blog/3539230