A. 蹄球锦标赛

题目描述

为了准备即将到来的蹄球锦标赛,Farmer John正在训练他的N头奶牛(方便起见,编号为1…N,其中1≤N≤100)进行传球。这些奶牛在牛棚一侧沿直线排列,第i号奶牛位于距离牛棚xi的地方(1≤xi≤1000)。每头奶牛都在不同的位置上。
在训练开始的时候,Farmer John会将若干个球传给不同的奶牛。当第i号奶牛接到球时,无论是从Farmer John或是从另一头奶牛传来的,她会将球传给最近的奶牛(如果有多头奶牛与她距离相同,她会传给其中距左边最远的那头奶牛)。为了使所有奶牛都有机会练习到传球,Farmer John想要确保每头奶牛都持球至少一次。帮助他求出为了达到这一目的他开始时至少要传出的球的数量。假设他在开始的时候能将球传给最适当的一组奶牛。

输入描述:

输入的第一行包含N。第二行包含N个用空格分隔的整数,其中第i个整数位xi

输出描述:

输出Farmer John开始的时候最少需要传出的球的数量,使得所有奶牛至少持球一次。

示例1

输入

5
7 1 3 11 4

输出

2

说明

在上面的样例中,Farmer John应该将球传给位于x=1的奶牛和位于x=11的奶牛。
位于x=1的奶牛会将她的球传给位于x=3的奶牛,在此之后这个球会在位于x=3的奶牛和位于x=4的奶牛之间来回传递。
位于x=11的奶牛会将她的球传给位于x=7的奶牛,然后球会被传给位于x=4的奶牛,在此之后这个球也会在位于x=3的奶牛和位于x=4的奶牛之间来回传递。
这样的话,所有的奶牛都会至少一次接到球(可能从Farmer John,也可能从另一头奶牛)。
可以看出,不存在这样一头奶牛,Farmer John可以将球传给她之后所有奶牛最终都能被传到球。


解答

给你N个点N条有向边,求在某些点开始跑边能够遍历整张图的最少点数

这已经是简化版的题意了,略去了前面没必要讲的建边(因为对于一个能看懂并写出AC代码的大佬来说这不是啥问题),建边唯一的坑点就是任意一头牛的左右两头牛距离相同连向左边(原题面括号那一句写错了)。

具体讲后面的操作:

如果一个点x连向y一条有向边,那么显然放在x比放在y更优,因为放x一定到y,放y不一定能到x

我们不妨做出一个假设:只有没有有向边连到的点是需要放球的

这已经非常接近正解了,但是很可惜,少考虑了一种情况

我们考虑这组数据
4
1 3 1 4
这样子相当于构造出了两个大小为二的环(事实上本题最大环大小就是二,但这不是重点,所以读者可以自证)

这样子显然要输出2,但是我们刚刚那样写会输出0

我们意识到应该把所有的环消除掉(比如缩成一个点)

所以我们使用tarjan缩点(可以参考洛谷或者其他OJ的题目,这里不过多说明)将图变为DAG(有向无环图)

然后统计一下有多少入度为0的点即可

时间复杂度:建边O(nlogn),缩点O(n)

总时间O(nlogn)

(可能有多余变量或数组,直接无视就好了)
#include<iostream>  #include<queue> #include<algorithm> #define maxn 100001  #define t edge[i].to #define s edge[i].nex using namespace std; struct Edge { int to,nex;
}edge[maxn],edg[maxn]; int st[maxn],dis[maxn],DFN[maxn],LOW[maxn],dye[maxn],vis[maxn],size[maxn],stack[maxn],sta[maxn],degree[maxn],dist[maxn],x1[maxn],y2[maxn]; int n,m,cnt,stacksize,color,dfsnum,c,a[2001],maxx; void addedge(int x,int y) {
    edge[++cnt].to=y;
    edge[cnt].nex=st[x];
    st[x]=cnt;
} void tarjan(int start) {
    vis[stack[++stacksize]=start]=1;
    LOW[start]=DFN[start]=++dfsnum; for(int i=st[start];i;i=s) if(!DFN[t])tarjan(t),LOW[start]=min(LOW[start],LOW[t]); else if(vis[t])LOW[start]=min(LOW[start],DFN[t]); if(DFN[start]==LOW[start])
    { int y; while(y=stack[stacksize--])
        {
            dye[y]=start;
            vis[y]=0; if(start==y)break;
            dis[start]+=dis[y];
        }
    }
} int main() { cin>>n;
    m=n; for(int i=1;i<=n;i++)
    { cin>>a[i];
    }
    sort(a+1,a+1+n);
    addedge(1,2);
    x1[1]=1;y2[1]=2;
    x1[n]=n;y2[n]=n-1;
    addedge(n,n-1); for(int i=2;i<n;i++)
    { if(a[i]-a[i-1]<=a[i+1]-a[i])addedge(i,i-1),x1[i]=i,y2[i]=i-1; else addedge(i,i+1),x1[i]=i,y2[i]=i+1;
    } for(int i=1;i<=n;i++)if(!DFN[i])tarjan(i); for(int i=1;i<=m;i++)
    { int x=dye[x1[i]],y=dye[y2[i]];
        maxx=max(maxx,max(x,y)); //cout<<x1[i]<<' '<<y1[i]<<' '<<x<<' '<<y<<endl; if(x!=y)
        {
            edg[++c].to=y;
            edg[c].nex=sta[x];
            sta[x]=c;
            degree[y]++;
        }
    } int ans=0; for(int i=1;i<=maxx;i++)
    { //cout<<degree[i]<<endl; if(!degree[i]&&dye[i]==i)ans++;
    } cout<<ans; return 0;
}

来源:千柒/Kan_kiz