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 }
View Code

凯少思路:

如果 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 }
View Code

 

因为 n 很小,所以对每个点跑两次DFS并不会超时;

但,如果 n 大了呢,那制定不能每个点跑两次DFS了,那该肿么办呢?

本蒟蒻还没想出来~~~~~~~~

据说,此题正解为求解两次拓扑序;

 

https://paste.ubuntu.com/p/xXxYdnDRBV/

posted @ 2019-05-13 21:39  HHHyacinth  阅读(748)  评论(0编辑  收藏  举报