题解

55 条题解

  • 0
    @ 2016-11-18 20:22:42
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int MAXN=200100;
    int n,to[MAXN],time[MAXN],ans=2*MAXN,preto[MAXN],vis[MAXN];
    void search(int x,int t)
    {
        if(time[to[x]]!=0){
            ans=min(ans,t-time[to[x]]+1);
            return;
        }
        else{
            time[x]=t;
            vis[x]=1;
            search(to[x],t+1);
        }
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&to[i]),preto[to[i]]=i;
        for(int i=1;i<=n;i++){
            if(preto[i]==0) continue;
            if(vis[i]) continue;
            memset(time,0,sizeof(time));
            time[i]=1;
            search(i,1);
        }
        printf("%d",ans);
        return 0;
    }
    
  • 0
    @ 2016-11-18 10:12:49

    先剪枝,不断剪掉入度为0的点,剩下的点肯定构成环。然后找最大环就行了应该是吧
    program message;
    var ru,chu:array[1..200000] of longint;
    i,n,ans:longint;
    procedure jianzhi;
    var
    dui:array[0..200000] of longint;
    tail,head,j:longint;
    begin
    tail:=0;
    head:=1;
    for j:=1 to n do
    if ru[j]=0 then
    begin
    inc(tail);
    dui[tail]:=j;
    dec(ru[chu[j]]);
    end;
    while head<=tail do
    begin
    if ru[chu[dui[head]]]<=0 then
    begin
    inc(tail);
    dui[tail]:=chu[dui[head]];
    dec(ru[chu[dui[tail]]]);
    end;
    inc(head);
    end;
    end;

    procedure dfs(x,st,i:longint);
    begin

    if ru[x]=0 then exit;
    ru[x]:=0;
    if (x=i)then begin
    if st<ans then ans:=st;
    exit;
    end;
    dfs(chu[x],st+1,i);
    end;

    begin
    assign(input,'www.in');
    reset(input);
    assign(output,'www.out');
    rewrite(output);
    readln(n);
    for i:=1 to n do ru[i]:=0;
    for i:=1 to n do
    begin
    read(chu[i]);
    inc(ru[chu[i]]);
    end;
    jianzhi;

    ans:=maxlongint;
    for i:=1 to n do
    if ru[i]>0 then dfs(chu[i],1,i);
    writeln(ans);
    close(input);
    close(output);
    end.

    • @ 2016-11-18 10:13:08

      是最小环...

  • 0
    @ 2016-11-17 21:11:53

    说的什么并查集。。太麻烦
    做法:
    会有很多入度为0的没用的枝杈,这些是不可能构成环的,所以不需要进行搜索,直接剪枝。
    然后 就是简单的图的dfs遍历 首先在主程序里面记录下全局变量st,记录开始搜索的节点编号,记录dfs到这一个点的步数便是从这一个点开始,能构成的环的大小,朴素做法就是都找一遍不断更新ans值。其实感觉可以再有一个剪枝 不过懒得想了= =
    ```pascal
    program mes;
    var
    t,inn:array [0..200020] of longint;
    n,ans,i,j,st,sum:longint;
    o:array [0..200020] of boolean;

    PROCEDURE dfs(x:longint);
    begin
    inc(sum);
    o[x]:=true;
    if (t[x]=st) then exit;
    dfs(t[x]);
    end;
    PROCEDURE del(x:longint);
    begin
    o[x]:=true;
    dec(inn[t[x]]);
    if (inn[t[x]] = 0) then del(t[x]);
    end;

    BEGIN
    fillchar(o,sizeof(o),false);
    ans:=200000;
    readln(n);
    for i:=1 to n do
    begin
    read(t[i]);
    inc(inn[t[i]]);
    end;
    for i:=1 to n do
    begin
    if (not o[i]) and (inn[i] = 0) then del(i);
    end;
    for i:=1 to n do
    begin
    if o[i]=false then
    begin
    st:=i;
    sum:=0;
    dfs(i);
    if sum<ans then ans:=sum;
    end;
    end;
    writeln(ans);
    END.
    ```

  • 0
    @ 2016-11-17 19:24:36

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int M = 200005;
    const int INF = 0x7fffffff;

    int f[M], vis[M], done[M], ans = INF;

    void dfs(int x, int tep){
    int bri = x;
    while(1){
    vis[x] = tep;
    if(vis[f[x]] && !done[f[x]]){
    ans = min(ans, tep - vis[f[x]] + 1);
    break;
    }
    if(done[f[x]]) break;
    x = f[x], tep++;
    }
    x = bri;
    while(1){
    done[x] = 1;
    if(done[f[x]]) return;
    x = f[x];
    }
    }

    int main()
    {
    freopen("in", "r", stdin);
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &f[i]);
    for(int i = 1; i <= n; i++)
    if(!done[i])
    dfs(i, 1);
    printf("%d", ans);
    return 0;
    }
    那年,怀恨,一把辛酸泪

  • 0
    @ 2016-11-15 13:09:58

    Topological sorting + DFS

    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #define MAXN 200000 + 10
    using namespace std;
    int n, m, in[MAXN], to[MAXN], vis[MAXN], minw = MAXN, node;
    queue<int> q;
    int dfs(int i, int sum) {
    vis[i] = 1;
    if(!vis[to[i]])
    dfs(to[i], sum + 1);
    else {
    minw = min(minw, sum + 1);
    }
    }
    int main() {
    ios::sync_with_stdio(false);
    cin >> n;
    for (int i = 0; i < n; i++) {
    int t;
    cin >> t;
    t--;
    in[t]++;
    to[i] = t;
    }
    for (int i = 0; i < n; i++) {
    if (in[i] == 0) {
    q.push(i);
    vis[i] = 1;
    }
    }
    while(!q.empty()) {
    int p = q.front();
    q.pop();
    in[to[p]]--;
    if(!in[to[p]]) {
    vis[to[p]] = 1;
    q.push(to[p]);
    }
    }
    for (int i = 0; i < n; i++) {
    node = 0;
    if (!vis[i] && in[i])
    dfs(i, 0);
    }
    cout << minw;
    return 0;
    }

  • 0
    @ 2016-11-14 21:11:54

    O(N)
    #include <cstdio>

    int n,p,sp,ans=99999999,t[210000],vis[210000]={0};

    void dfs(int u,int step){
    vis[u]=1;
    if(!vis[t[u]])
    dfs(t[u],step+1);
    else
    p=t[u],sp=step;
    if(u==p)
    ans=ans<(sp-step)?ans:(sp-step);

    }

    int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&t[i]);
    for(int i=1;i<=n;i++)
    if(!vis[i]) dfs(i,0);
    printf("%d",ans+1);
    return 0;
    }

  • 0
    @ 2016-11-08 15:58:35

    var
    a,b,c:array [0..200005] of longint;
    n,t,i,ans,p,s,x:longint;
    function min(x,y:longint):longint;
    begin if (x<y) then exit(x) else exit(y); end;
    begin
    readln(n); t:=0; p:=0; ans:=1000000;
    for i:=1 to n do read(a[i]);
    for i:=1 to n do
    if (b[i]=0) then
    begin
    x:=i; s:=0;
    while true do begin if (b[x]<>0) then break; inc(s); b[x]:=1; c[x]:=s; x:=a[x]; end;
    if (b[x]=1) and (s-c[x]+1>1) and (s-c[x]+1<ans) then ans:=s-c[x]+1;
    x:=i;
    while true do begin if (b[x]=2) then break; b[x]:=2; x:=a[x]; end;
    end;
    writeln(ans);
    end.

  • 0
    @ 2016-11-07 18:42:13

    //HBat
    //找环,找过的不用再找。
    #include <iostream>
    #include <cstdio>
    #include <cstring>

    using namespace std;

    #define MAXN 200001

    int minlong=0x7fffff;//最小环
    int n;//人数
    int nextp[MAXN];//告诉的人
    int blot[MAXN];//已经访问过的,并标记地几次访问
    int nblot[MAXN];//在一组访问中的第几个人

    int main()
    {
    int i,temp,k,kc;
    int ls=0;//标记已经访问的人数,当ls==n就可以结束了
    memset(blot,0,sizeof(blot));//初始化
    cin>>n;
    for(i=1;i<=n;++i)
    cin>>nextp[i];
    kc=0;//访问组标号
    k=1;//查找环开始人
    while(ls<n&&k<=n)//ls<n
    {
    temp=k;//进入查找环
    i=0;//在一组访问中的第几个人
    ++kc;
    do
    {
    i++;//在一组访问的第i个人
    if(blot[temp]==0)ls++;//如果从来没有访问过就记录
    blot[temp]=kc;//入组
    nblot[temp]=i;//组名次
    temp=nextp[temp];//下一个
    if(blot[temp]==kc)//看是否构成一个环,构成,结束
    {
    minlong=minlong<(i-nblot[temp]+1)? minlong:(i-nblot[temp]+1);
    break;
    }
    else if(blot[temp]!=0)break;//如果以前的组访问过就不需要再访问了,否则循环
    }while(i<=n&&ls<n);//ls<n
    if(ls>=n)break;//ls<n
    while(k<=n)//搜索下一个查找点
    if(blot[++k]!=0)continue;
    else break;
    }
    cout<<minlong;//输出
    return 0;
    }

  • 0
    @ 2016-11-04 23:36:34

    可证明环与环无交叉不联通
    去掉非环点
    然后想怎么搜怎么搜
    模拟都能过
    var a,num:array[1..200000]of longint;
    i,j,n,x,x1,x2,min:longint;
    procedure dfs(x:longint);
    begin
    if num[x]=0 then
    begin
    dec(num[a[x]]);
    dfs(a[x]);
    a[x]:=0;
    end
    else exit;
    end;

    begin
    readln(n);
    for i:=1 to n do
    begin
    read(x);
    a[i]:=x;
    inc(num[x]);
    end;
    for i:=1 to n do
    if num[i]=0 then dfs(i);
    min:=n+1;
    for i:=1 to n do
    begin
    x:=0;
    if a[i]>0 then
    begin
    x1:=i;
    while a[x1]>0 do
    begin
    x2:=x1;
    x1:=a[x1];
    a[x2]:=0;
    inc(x);
    end;
    if min>x then min:=x;
    end;
    end;
    write(min);
    readln;readln;
    end.

  • 0
    @ 2016-11-03 19:55:27

    先去掉一些入度为零的点,再用深度搜索
    program lij;
    const maxn=200001;
    var link,ind,cc:array[0..maxn]of longint;
    f1,f2:array[0..maxn]of boolean;
    i,n,m,ans,min:longint;
    procedure readin;
    begin
    readln(n);
    for i:=1 to n do
    begin
    read(link[i]);
    inc(ind[link[i]]);
    end;
    for i:=1 to n do
    f1[i]:=(ind[i]=0);
    end;
    procedure dfs(i:longint);
    var j:longint;
    begin
    inc(m);cc[i]:=m;
    f1[i]:=true;f2[i]:=true;
    j:=link[i];
    if f2[j]=false then
    begin
    if f1[j]=false then
    dfs(j)
    end
    else
    begin
    ans:=m-cc[j];
    if ans<min then
    min:=ans;
    end;
    end;
    begin
    min:=maxlongint;
    fillchar(ind,sizeof(ind),0);
    readin;
    i:=1;
    while true do
    begin
    while f1[i]=true do
    inc(i);
    if i<=n then
    begin
    fillchar(cc,sizeof(cc),0);
    fillchar(f2,sizeof(f2),0);
    m:=0;
    dfs(i);
    end
    else break;
    end;
    writeln(min+1);
    end.

  • 0
    @ 2016-10-29 20:05:22

    最后一个点爆栈

    • @ 2016-11-14 20:30:51

      dfs前加 inline

  • 0
    @ 2016-10-16 11:40:32

    先找环,再算长度...
    、、、c++
    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int N=200010;
    int to[N];
    int visited[N],k=1;

    int main(){
    int n,Min=99999999;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&to[i]);
    for(int i=1;i<=n;i++){
    if(!visited[i]){
    int nown=i;
    while(!visited[nown]){
    visited[nown]=k;
    nown=to[nown];
    }
    if(visited[nown]!=k){
    k++; continue;
    }
    int start=nown,cnt=0;
    nown=to[nown];
    while(nown!=start)cnt++,nown=to[nown];
    cnt++;
    if(cnt<Min)Min=cnt;
    k++;
    }
    }
    printf("%d",Min);
    return 0;
    }
    、、、

  • 0
    @ 2016-10-04 09:29:26

    哎,现在的TG怎么这么水QAQwww

  • 0
    @ 2016-09-14 17:38:16
    #include<iostream>
    #include<cstdio>
    using namespace std;
    int ans=99999999,n,p[200050]={0},t[200050],used[200050],time[200050];
    void dfs(int now,int step,int cnt){
        used[now]=1;
        //记录当前步数 
        time[now]=step;
        //注意要标记是否从同一个点出发 
        p[now]=cnt;
        if(!used[t[now]]){
            dfs(t[now],step+1,cnt);
        }
        else{
            if(p[t[now]]==cnt){
                ans=min(time[now]-time[t[now]]+1,ans);//环的长度为两部数差 
            }
        }
    }
    int main(){
        freopen("in.txt","r",stdin);
        cin>>n;
        int i,step;
        for(i=1;i<=n;i++){
            cin>>t[i];
            used[i]=0;
        }
        for(i=1;i<=n;i++){
            //每个点实际上只需被访问一次 
            if(!used[i]){
                step=1;
                dfs(i,step,i);
            }
        }
        cout<<ans;
        return 0;
    }
    ```
    
  • 0
    @ 2016-09-12 19:16:38

    编译器爸爸告诉我不能用next,会有歧义就CE了两次QWQ
    并查集+dfs
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define inf 210000000
    #define N 200005
    using namespace std;
    int n,ans,y;
    int f[N],nxt[N],h[N];
    bool b[N];
    int find(int x){
    if(x==f[x]) return x;
    f[x]=find(f[x]);
    return f[x];
    }
    void merge(int x,int y){
    int f1=find(x);
    int f2=find(y);
    f[f1]=f2;
    return ;
    }
    void dfs(int x,int dep){
    b[x]=true;h[x]=dep;
    if(!b[nxt[x]])
    dfs(nxt[x],dep+1);
    else{
    int k=h[x]-h[nxt[x]]+1;
    ans=min(ans,k);
    }

    return ;
    }
    int main(){
    scanf("%d",&n);
    ans=inf;
    for(int i=1;i<=n;i++)
    f[i]=i;
    for(int i=1;i<=n;i++){
    scanf("%d",&y);
    nxt[i]=y;
    if(find(i)!=find(y))
    merge(i,y);
    else{
    memset(b,0,sizeof(b));
    dfs(i,1);
    }
    }
    printf("%d",ans);
    return 0;

    }

  • 0
    @ 2016-08-31 17:46:43

    测试数据 #0: Accepted, time = 0 ms, mem = 3160 KiB, score = 10

    测试数据 #1: Accepted, time = 0 ms, mem = 3156 KiB, score = 10

    测试数据 #2: Accepted, time = 0 ms, mem = 3160 KiB, score = 10

    测试数据 #3: Accepted, time = 0 ms, mem = 3160 KiB, score = 10

    测试数据 #4: Accepted, time = 15 ms, mem = 3156 KiB, score = 10

    测试数据 #5: Accepted, time = 0 ms, mem = 3156 KiB, score = 10

    测试数据 #6: Accepted, time = 31 ms, mem = 3156 KiB, score = 10

    测试数据 #7: Accepted, time = 31 ms, mem = 3160 KiB, score = 10

    测试数据 #8: Accepted, time = 31 ms, mem = 3156 KiB, score = 10

    测试数据 #9: Accepted, time = 62 ms, mem = 3160 KiB, score = 10

    Accepted, time = 170 ms, mem = 3160 KiB, score = 100

    代码

    var
    b,t,a:array[1..200000] of longint;
    top,min,n,next,pre,x,i,j:longint;
    begin
    readln(n);
    for i:=1 to n do read(t[i]);
    for i:=1 to n do b[i]:=0;
    top:=0;
    min:=999999;
    for j:=1 to n do
    begin
    if (b[j]=0 )and (b[t[j]]=0) then
    begin
    pre:=j;
    b[j]:=1;
    top:=top+1;
    a[top]:=j;
    while b[t[pre]]=0 do
    begin
    pre:=t[pre];
    b[pre]:=1;
    top:=top+1;
    a[top]:=pre;
    end;
    top:=top+1;
    a[top]:=t[pre];
    x:=a[top];
    for i:=1 to top do
    begin
    if a[i]=x then

    begin
    if (top-i)<min then min:=top-i;
    break;
    end;
    end;
    end;
    end;
    writeln(min);
    end.

  • 0
    @ 2016-08-24 11:20:02

    水题……

    评测结果

    编译成功

    测试数据 #0: Accepted, time = 0 ms, mem = 2316 KiB, score = 10

    测试数据 #1: Accepted, time = 0 ms, mem = 2316 KiB, score = 10

    测试数据 #2: Accepted, time = 0 ms, mem = 2320 KiB, score = 10

    测试数据 #3: Accepted, time = 0 ms, mem = 2324 KiB, score = 10

    测试数据 #4: Accepted, time = 15 ms, mem = 2320 KiB, score = 10

    测试数据 #5: Accepted, time = 15 ms, mem = 2320 KiB, score = 10

    测试数据 #6: Accepted, time = 187 ms, mem = 2800 KiB, score = 10

    测试数据 #7: Accepted, time = 203 ms, mem = 2840 KiB, score = 10

    测试数据 #8: Accepted, time = 203 ms, mem = 2800 KiB, score = 10

    测试数据 #9: Accepted, time = 234 ms, mem = 2748 KiB, score = 10

    Accepted, time = 857 ms, mem = 2840 KiB, score = 100

    本蒟蒻是酱紫做的……
    先拓扑去掉所有的非环上节点……(因为他们不可能从别人口中得知任何人的生日,他们是不可能的),然后留下的环都是简单环……然后递归获取环长,答案就是环长中的最小值。
    这个算法不是最优的,但是857MS还是挺快的。
    代码如下
    c++
    #include <iostream>
    #include <utility>
    #include <queue>
    #include <climits>
    using namespace std;
    int n,ans=INT_MAX;
    bool book[200005];
    pair<int,int> p[200005];
    queue<int> que;
    int roundl(int x,int dx,int total)
    {
    book[x]=1;
    if(x==dx)return total+1;return roundl(p[x].first,dx,total+1);
    }
    int main()
    {
    ios::sync_with_stdio(0);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
    cin>>p[i].first;
    p[p[i].first].second++;
    }
    for(int i=1;i<=n;i++)if(!p[i].second)que.push(i);
    while(!que.empty())
    {
    book[que.front()]=1;
    if(!--p[p[que.front()].first].second)que.push(p[que.front()].first);
    que.pop();
    }
    for(int i=1;i<=n;i++)if(!book[i])ans=min(ans,roundl(p[i].first,i,0));
    cout<<ans;
    return 0;
    }

  • 0
    @ 2016-07-20 23:15:25

    思路:从每个点dfs,标记访问过的点,
    如果在搜索中遇到访问过的,回溯,
    看能否构成环,若能,则比较环的长度与ans,更新ans

    开始打出来,过9个 第十个RE
    加个inline就好了
    #include <cstdio>

    int n,t[200050];
    int visited[200050]={0};
    int mindist=200088;
    int node;

    inline int dfs(int i,int dist){
    visited[i]=1;
    if(!visited[t[i]])
    dist=dfs(t[i],dist);
    else
    node=t[i];
    if(node==i)
    mindist=mindist<dist?mindist:dist;

    return dist+1;
    }

    int main(){
    // freopen("in.txt","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&t[i]);
    for(int i=1;i<=n;i++){
    node=0;
    if(!visited[i])
    dfs(i,0);
    }
    printf("%d",mindist+1);

    return 0;
    }

  • 0
    @ 2016-07-15 20:02:34

    scanf有毒,深刻地体会到cin的慢了

    #include<iostream>
    #include<cstring>
    #define maxn 200005
    using namespace std;
    
    int n,u[maxn],minium = 0x7fffffff,time[maxn],now = 1;
    //memset(time,0,sizeof(time));
    bool been[maxn];
    void dfs(int p){
        memset(been,0,sizeof(been));
        while(!time[p]){
            time[p]=now++;
            been[p]=1;
            if(been[u[p]]){
                minium=min(time[p]-time[u[p]]+1,minium);
                break;
            }
            p = u[p];
        }
    }
    
    int main(){
        freopen("message.in","r",stdin);
        scanf("%d",&n);
        for(int i = 1;i<=n;i++)scanf("%d",&u[i]);
        for(int i = 1;i<=n;i++){
            if(!time[i])dfs(i);
        }
        printf("%d\n",minium);
        return 0;
    }
    
  • 0
    @ 2016-07-13 16:36:31

    本题在并查集的基础上增加路径长度的判断。当有向边<x,y>的两个顶点不在同一集合时,利用并查集算法将x,y两个结点合并起来。设cnt[x]代表结点x经过cnt[x]次的合并才能并入结点y所在集合。则cnt[x]:=cnt[y]+1;
    program vj1979;
    var
    father,t,cnt:array[1..200000] of longint;
    ans,n,i,j:longint;
    function min(a,b:longint):longint; //求两数中的最小值
    begin
    if a<b then exit(a); exit(b);
    end;
    function getfather(v:longint):longint; //查找v元素所在集合中的根,cnt[v]表示V结点在该集合中的深度
    var tmp:longint;
    begin
    if father[v]=v then exit(v);
    tmp:=father[v];
    father[v]:=getfather(father[v]);
    cnt[v]:=cnt[v]+cnt[tmp];
    exit(father[v]);
    end;
    function judge(x,y:longint):boolean;
    var xx,yy:longint;
    begin
    xx:=getfather(x);
    yy:=getfather(y);
    exit(xx=yy);
    end;
    procedure merge(x,y:longint);//合并集合
    var xx,yy:longint;
    begin
    xx:=getfather(x);
    yy:=getfather(y);
    father[xx]:=yy;
    cnt[x]:=cnt[y]+1;
    end;
    begin
    readln(n);
    for i:=1 to n do begin father[i]:=i; cnt[i]:=0; end;
    ans:=maxlongint;
    for i:=1 to n do begin
    read(t[i]);
    if not judge(i,t[i]) then merge(i,t[i]) else ans:=min(ans,cnt[i]+cnt[t[i]]+1);
    end;

    writeln(ans);
    end.

信息

ID
1979
难度
6
分类
(无)
标签
递交数
4096
已通过
980
通过率
24%
被复制
9
上传者