1. 程式人生 > >Codeforces Round #541 (Div. 2) D(並查集+拓撲排序) F (並查集)

Codeforces Round #541 (Div. 2) D(並查集+拓撲排序) F (並查集)

names kit con ++ const union cto mes continue

D. Gourmet choice

鏈接:http://codeforces.com/contest/1131/problem/D

思路: = 的情況我們用並查集把他們扔到一個集合,然後根據 > < 跑拓撲排序,根據拓撲排序的結果從小到大填數字就好了,需要註意的細節寫在代碼註釋裏了

代碼:

#include<bits/stdc++.h>
using namespace std;
const int M = 2e3+10;
int f[M],n,m;
set<int>st[M];
vector<int>v;
int vis[M];
map
<int,int>ans; bool flag; string g[M]; int in[M]; int Find(int x){ if(x == f[x]) return x; return f[x] = Find(f[x]); } int Union(int x,int y){ int fx = Find(x); int fy = Find(y); if(fx != fy) f[fx] = fy; } void Toposort(){ queue<int>q; for(int
i = 0;i < n+m;i ++){ //同一個集合內的數如果之前已經出現過就跳過 if(vis[Find(i)]==1) continue; if(in[Find(i)] == 0){ q.push(Find(i)); vis[Find(i)]=1; } } //因為要計數,所以多開一個vector存每一輪入度為0的點 int cnt = 1; while(!q.empty()){ v.clear();
while(!q.empty()){ v.push_back(q.front()); q.pop(); } for(auto &i : v){ ans[i] = cnt; for(auto &j : st[i]){ if(vis[j]==1) continue; in[j]--; if(in[j] == 0){ q.push(j); vis[j]=1; } } } cnt ++; } } int main() { cin>>n>>m; flag = 1; for(int i = 0;i < n+m;i ++) f[i] = i; for(int i = 0;i < n;i ++){ cin>>g[i]; for(int j = 0;j < m;j ++){ if(g[i][j] == =){ if(Find(i) != Find(n+j)) Union(i,n+j); } } } for(int i = 0;i < n;i ++){ for(int j = 0;j < m;j ++){ if(g[i][j] == =) continue; int x = Find(i),y = Find(n+j); if(x == y) flag = 0; if(g[i][j] == >) swap(x,y); if(st[x].find(y) == st[x].end()){ st[x].insert(y); in[y]++; } } } //如果一個集合內出現>或<,那麽自相矛盾,輸出No if(!flag){ cout<<"No"<<endl; return 0; } Toposort(); //如果兩個數 < > 關系矛盾,也就是i出現x>y且x<y,那麽雙方入度都無法為0,無法取值, //所以如果值為0那麽代表出現矛盾的情況輸出N0 for(int i = 0;i < n+m;i ++){ if(ans[Find(i)] == 0) flag = 0; } if(flag){ cout<<"Yes"<<endl; for(int i = 0;i < n;i ++) cout<<ans[Find(i)]<<" "; cout<<endl; for(int i = n;i < n+m;i ++) cout<<ans[Find(i)]<<" "; cout<<endl; } else cout<<"No"<<endl; return 0; }

F. Asya And Kittens

鏈接:http://codeforces.com/contest/1131/problem/F

思路:很容易發現房間合並的過程非常像並查集集合的合並,但是並查集合並並不能得到一段序列,所以我們用一個nex數組儲存數字的前後關系,並用last數組記錄集合最後的那個數,兩個集合合並時,左邊集合最後那個數的nex就是右邊集合的第一個數,合並後整個集合的last就是右邊集合的last,這樣維護好兩個數組,最後遍歷一遍nex數組就可以得到一段符合要求的序列了。

實現代碼:

#include<bits/stdc++.h>
using namespace std;
const int M = 2e5+10;
int f[M],nex[M],last[M];
int Find(int x){
    if(x == f[x]) return x;
    return f[x] = Find(f[x]);
}

int Union(int x,int y){
    int fx = Find(x);
    int fy = Find(y);
    if(fx != fy) f[fy] = fx;
}

int main()
{
    int n,a,b;
    cin>>n;
    for(int i = 1;i <= n;i ++)
        last[i]=f[i]=i;
    for(int i = 1;i <= n-1;i ++){
        cin>>a>>b;
        int fx = Find(a);
        int fy = Find(b);
        nex[last[fx]] = fy;
        last[fx] = last[fy];
        Union(a,b);
    }
    for(int i = Find(1);i;i=nex[i])
        cout<<i<<" ";
    return 0;
}

Codeforces Round #541 (Div. 2) D(並查集+拓撲排序) F (並查集)