zoukankan      html  css  js  c++  java
  • 洛谷 P2835 刻录光盘

      其实这题水的一批...............

    题目描述

    在JSOI2005夏令营快要结束的时候,很多营员提出来要把整个夏令营期间的资料刻录成一张光盘给大家,以便大家回去后继续学习。组委会觉得这个主意不错!可是组委会一时没有足够的空光盘,没法保证每个人都能拿到刻录上资料的光盘,又来不及去买了,怎么办呢?!

    组委会把这个难题交给了LHC,LHC分析了一下所有营员的地域关系,发现有些营员是一个城市的,其实他们只需要一张就可以了,因为一个人拿到光盘后,其他人可以带着U盘之类的东西去拷贝啊!

    可是,LHC调查后发现,由于种种原因,有些营员并不是那么的合作,他们愿意某一些人到他那儿拷贝资料,当然也可能不愿意让另外一些人到他那儿拷贝资料,这与我们JSOI宣扬的团队合作精神格格不入!!!

    现在假设总共有N个营员(2<=N<=200),每个营员的编号为1~N。LHC给每个人发了一张调查表,让每个营员填上自己愿意让哪些人到他那儿拷贝资料。当然,如果A愿意把资料拷贝给B,而B又愿意把资料拷贝给C,则一旦A获得了资料,则B,C都会获得资料。

    现在,请你编写一个程序,根据回收上来的调查表,帮助LHC计算出组委会至少要刻录多少张光盘,才能保证所有营员回去后都能得到夏令营资料?

    输入输出格式

    输入格式:

    先是一个数N,接下来的N行,分别表示各个营员愿意把自己获得的资料拷贝给其他哪些营员。即输入数据的第i+1行表示第i个营员愿意把资料拷贝给那些营员的编号,以一个0结束。如果一个营员不愿意拷贝资料给任何人,则相应的行只有1个0,一行中的若干数之间用一个空格隔开。

    输出格式:

    一个正整数,表示最少要刻录的光盘数。

    输入输出样例

    输入样例#1:
    5
    2 3 4 0
    4 5 0
    0
    0
    1 0
    输出样例#1:
    1

    
    

      首先,这题有两种做法:并查集可以水过,然而太监就正解完事了啊。

    所以首先说一下Tarjan的做法:

      我们先建个图,然后就可以很显然的发现:

    1. 一个环只需要一张光盘QAQ。
    2. 只有每个入度为0的点需要光盘!

      那么我们的思路就很好发现了丫,我们先缩个点,然后就可以很容易的统计一下入度为零的个数啊耶!

      至此本题完结。

     放上代码。

    #include<bits/stdc++.h>
    using namespace std;
    int n;
    int dfn[205],low[205];
    int cnt;
    bool ins[205];
    int head[205],tot,s[205],tp,k;
    int inn[205],chu[205],hao[205];
    bool vis[205],viss[205];
    struct edge{
        int to;
        int nxt;
    }a[100000],b[100000];
    void add(int u,int v){
        a[++tot].to=v;
        a[tot].nxt=head[u];
        head[u]=tot;
    }
    void tarjan(int x){
        dfn[x]=low[x]=++cnt;
        s[++tp]=x;ins[x]=true;
        for(int i=head[x];i;i=a[i].nxt) {
            if(!dfn[a[i].to]){
                tarjan(a[i].to);
                low[x]=min(low[x],low[a[i].to]);
            }
            else if(ins[a[i].to]==true)
            low[x]=min(low[x],dfn[a[i].to]);    
        }
        if(dfn[x]==low[x]) {
            k++;
            int y;
            do{
                 y=s[tp];
            tp--;
            ins[y]=false; hao[y]=k; }while(x!=y); } } int main() { cin>>n; for(int i=1;i<=n;i++) { int x; cin>>x; while(x!=0){ add(i,x); cin>>x; } } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int x=1;x<=n;x++) for(int i=head[x];i;i=a[i].nxt) { int y=a[i].to; if(hao[x]==hao[y]) continue; inn[hao[y]]=1; chu[hao[x]]=1; } int ans=0,ans2=0; for(int i=1;i<=n;i++) if(inn[hao[i]]==0&&!vis[hao[i]]) {
            ans++;
            vis[hao[i]]=true;
         }
    if(k==1) cout<<1<<endl; else cout<<ans<<endl; return 0; }

    找爸爸 并查集

      很多人会发现并查集时会将两个点结成双向的边。即本来只能A是B的爸爸,但是并查集之后B也是A的爸爸了显然哪里有点问题

    所以这样就会导致最终统计的光盘数减少

      其实我们只需要把并查集改一改,合并操作略有变化.改成“单向并查集”.

      如果x愿意把光盘分享给y,就合并x的根和y本身,可以这么理解:x的根分享给x,x再分享给y

      原版的并查集是合并x、y的根,这里不能这么做,因为这么做即表示:x的根可以分享给y的根(然而并不是)

      此处还用了floyd转移关系。

    特别感谢代码提供者!!

    #include <iostream>
    using namespace std;
    
    int G[210][210];
    int N, ans;
    
    int f[210];   //父亲 
    int ufs[210]; //结点是否为并查集的根 
    
    int Find(int x) {
        while(x != f[x]) x = f[x];
        return x;
    }
    
    void Unite(int x, int y) {
        while(x != f[x]) x = f[x]; //找到x的根
        f[y] = x; //x是y的父亲
    } 
    
    int main() {
        int tmp;  
        cin >> N;
        for(int i=1; i<=N; i++) f[i] = i; //并查集初始化 
        for(int i=1; i<=N; i++) {
            while(true) {
                cin >> tmp;
                if(!tmp) break;
                G[i][tmp] = true;
            }
        }
        for(int k=1; k<=N; k++) //Floyd
            for(int i=1; i<=N; i++)
                for(int j=1; j<=N; j++)
                    G[i][j] = G[i][j] || (G[i][k] && G[k][j]);
        for(int i=1; i<=N; i++)
            for(int j=1; j<=N; j++)
                if(G[i][j]) Unite(i, j);
        for(int i=1; i<=N; i++) ufs[Find(i)] = 1;
        for(int i=1; i<=N; i++) if(ufs[i]) ans ++;
        cout << ans << endl; 
        return 0;
    }

    最后,此处链接一种很神奇的方法!!-->点我点我

    至此完结,谢谢!

  • 相关阅读:
    淘宝Banner 轮播图
    JavaScript move简易版运动框架封装
    javaScript 导航栏
    JS 运动框架完整版
    Js 数组操作
    JS 动画轮播效果
    JavaScritpt 字符串操作
    Java AOP切面编程方式
    时间版 运动框架
    Nmon的安装及使用
  • 原文地址:https://www.cnblogs.com/qxyzili--24/p/10529783.html
Copyright © 2011-2022 走看看