1. 程式人生 > >920E Connected Components?

920E Connected Components?

這題剛開始想用並查集,因為具體只要點的合併,不需要知道每條邊的權值,所以給了個錯誤答案:

//I(並查集--wa了)
#include<stdio.h>
#include<string.h>
#include<map>
#include<algorithm>
using namespace std;
#define MAXN 200010

map<int,int>R;
int vis[MAXN];
int res[MAXN];
int pre[MAXN];

int Find(int x)
{
    if(pre[x]!=x)
        pre[x]=Find(pre[x]);
    return pre[x];
}

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

int main()
{
    int i,j;
    int n,m;
    int x,y;
    int cnt,ans,Count;
    int fx,fy;

    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            R[x]=y;
            R[y]=x;
        }
        for(i=1;i<=n;i++)
            pre[i]=i;
        for(i=1,cnt=0;i<=n&&cnt<n;i++)//建圖
        {
            cnt++;
            for(j=i+1;j<=n;j++)
            {
                if(R[i]!=j&&R[j]!=i)
                {
                    fx=pre[i];
                    fy=pre[j];
                    if(fx!=fy)
                    {
                        Union(i,j);
                        cnt++;
                    }
                }
            }
        }
        for(i=1,ans=Count=0;Count<n&&i<=n;i++)
        {
            if(!vis[i])
            {
                vis[i]=cnt=1;
                ans++;
                Count++;
                for(j=i+1;j<=n;j++)
                {
                    fx=pre[i];
                    fy=pre[j];
                    if(fx==fy)
                    {
                        cnt++;
                        Count++;
                        vis[j]=1;
                    }
                }
                res[ans]=cnt;
            }
        }
        sort(res+1,res+1+ans);
        printf("%d\n",ans);
        printf("%d",res[1]);
        for(i=2;i<=ans;i++)
            printf(" %d",res[i]);
        printf("\n");
    }
    return 0;
}

仔細分析一下,其實是錯在:

比如1和2可以有邊,2和4可以有邊,雖然最後確實是放在一個集合裡面了,但是這裡卻記錄了2個連通分量。。。。

正確思路網上有很多種,參考了一個大佬的:

講幾個晦澀點:

(1)

這裡加進去的時候不能是標記vis,因為這些點是後來還要用的(畢竟是要算補圖的,和只算原圖的全部都反過來了)

(2)

因為ss是記錄還未找到連通分量的(就在補圖中),而刪去ops的原因是:

也是swap的原因。

請記住,補圖的操作一切都和原圖中的相反,在原圖中是將已經遍歷過的點加入佇列,即:

這些點,但是補圖反過來,就是沒有被遍歷過的點,就是刪除ops中有的點剩下來的點。

還有,最後要swap,因為在for裡面被遍歷到的點,在原圖中是有邊相連的,但是在補圖中反過來,就沒有邊相連了,所以ss要和ops進行swap操作。

還有:

如果沒有這一步,就會MLE,這時將外面的ops或者ss拿到主函式裡面就可以過,

但是更好的方法是加一個清空,因為這個還有一個原因,就是使得

這一步更加容易理解,也更加沒有爭議。

(但是聽說一般set用的是棧記憶體,和放在哪裡沒有關係,那麼調整位置就AC的原因不詳。。。)

程式碼:

//I
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<set>
#include<vector>
#include<queue>
using namespace std;
#define MAXN 200001

vector<int>V[MAXN];//儲存每個點的臨邊(原圖中的)
set<int>ss,ops;//分別表示在原圖中是獨立的連通分量(即在補圖中是整體連通的)和在原圖中是整體(在補圖中是獨立的)
queue<int>q;
int ans[MAXN];//記錄每個連通分量中的點的個數
bool vis[MAXN];

int main()
{
    int i,j;
    int n,m;
    int u,v;
    int cnt;

    scanf("%d%d",&n,&m);

        memset(V,0,sizeof(V));
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            V[u].push_back(v);
            V[v].push_back(u);
        }
        for(i=1;i<=n;i++)
            ss.insert(i);//ss的初始化
        cnt=0;
        memset(vis,0,sizeof(vis));
        memset(ans,0,sizeof(ans));
        while(!q.empty())
            q.pop();
        for(i=1;i<=n;i++)
        {
            if(vis[i])
                continue;
            q.push(i);
            cnt++;
            while(!q.empty())
            {
                u=q.front();
                q.pop();
                if(vis[u])
                    continue;
                vis[u]=true;
                ans[cnt]++;
                //set<int>ops;
                for(j=0;j<V[u].size();j++)
                {
                    if(!vis[V[u][j]])
                        ops.insert(V[u][j]);
                }
                set<int>::iterator it;
                for(it=ops.begin();it!=ops.end();it++)
                    ss.erase(*it);
                for(it=ss.begin();it!=ss.end();it++)
                    q.push(*it);
                swap(ss,ops);//ss是還未連通的點的集合,因為刪去ops中的點後剩餘的點已經與點u連通了(在一個集合中,但是他們內部可能不是兩兩有直接連邊)
                ops.clear();
            }
        }
        sort(ans+1,ans+1+cnt);
        printf("%d\n",cnt);
        printf("%d",ans[1]);
        for(i=2;i<=cnt;i++)
            printf(" %d",ans[i]);
        printf("\n");

    return 0;
}