圆桌会议(圆排列)

来源:互联网 发布:淘宝首页添加旺旺 编辑:程序博客网 时间:2024/05/29 21:18

圆桌会议

题目描述

N个人顺时针围在一圆桌上开会,他们对身高很敏感.因此决定想使得任意相邻的两人的身高差距最大值最小.如果答案不唯一,输出字典序最小的排列,指的是身高的排列.

输入格式 1864.in

多组测试数据。第一行:一个整数ng, 1 <= ng <= 5.表示有ng组测试数据。
每组测试数据格式如下:
第一行: 一个整数N, 3 <= N <=50
第二行, 有个N整数, 第i个整数表示第i个人的身高hi,1<=hi<=1000. 按顺指针给出N个人的身高. 空格分开。

输出格式 1864.out

字典序最小的身高序列,同时满足相邻的两人的身高差距最大值最小。共ng行,每行对应一组输入数据。

输入样例 1864.in

2
5
1 3 4 5 7
4
1 2 3 4

输出样例 1864.out

1 3 5 7 4
1 2 4 3

    这题在考试的时候磨了我很久,作为一个正常人的第一想法就是二分+贪心。各种办法都试过,似乎都不靠谱,交上去怎么样都是0。

    后来上网查题解,各种五花八门的都有,说实话,当时没有想过用DP,结果搜出了DP的题解,不过居多的还是贪心,但是各种博客的贪心都有所不同。果断选择弃DP,选贪心。

友情链接:

    http://blog.csdn.net/kewpie_007/article/details/6273895

    最后参考了这篇博客的做法,虽然比较玄学,但还是很有道理的,用的是纯贪心。

纯贪心正解:

    举个例子:n=6,a[i]分别为2 5 7 9 15 19。先排序。


1、用贪心求解最小的最大身高差

    首先进来一个2和一个5,接着是7, 必然放在2和5中间是最优的,为什么呢?(此时为2 7 5)接着看。

到9的时候,如果接在5的后面,那么与前面2的差距又会变大了;如果放在2和7之间,与7的差距为2,但是与2的差距会变大为5;而放在7和5之间,与7的差距不变,但是与5的差距明显变小。(此时为2 7 9 5)

    如果还不够明确,继续看15,如果把15放在最后面,明显与2的身高差会特别大。如果放在2与7之间,与2的差距同样是很大的;放在7与9之间,与7的差距不变,但是与9的差距明显会比与2的差距小;但是放在9和5的中间,与9的差距不变,但是与5的差距明显会比与7的差距大。

    现在规律就很明确了吧,最好的插入法就是放在原序列第一大数和第二大数之间。然后把最终序列取一个最大差值即可。


2、用贪心排出字典序

    同样是上面的例子2 5 7 9 15 19,此时已经求出了最小的最大身高差为10。

    首先定义一个id,为一开始与之比较的值,id=1。

    同样是模拟一边就知道算法的准确性了。首先进来2、5、7、9都没毛病,都没有超过10,所以这样子放进来一定是字典序,因为这些数字是已经事先排好序的。

    15- 2,然后就开始有问题了,我们只能把9挑出来,因为9是与2的差值在10以内最大的数,于是要把15放在数列的最后(标记一下),这样既没有违反合法性又可以满足字典序。把此时的id标记为4(9的下标号)。

    后面的数是和9进行比较,如果发现超过了,就把超过的前一个合法值扔到后面去(注意不是最后面!!如果最后面有值了,就一直往前递减,tail一开始为n),此时也标记一下。

    最后扫一次如果看到一个标记v为1的,说明它要被扔到后面,ans[tail--]=a[i],否则就一直顺着排下去,ans[head++]=a[i]。

#include#include#include#includeusing namespace std;const int maxn=55;int n,a[maxn],mina,ng,ans[maxn];bool v[maxn];int main(){freopen("1864.in","r",stdin);freopen("1864.out","w",stdout);cin>>ng;while(ng--){cin>>n;for(int i=1;i<=n;i++) cin>>a[i];sort(a+1,a+1+n);memset(v,false,sizeof(v));memset(ans,0,sizeof(ans));//注意要初始化mina=a[2]-a[1];for(int i=3;i<=n;i++) mina=max(mina,a[i]-a[i-2]);//求最小的最大身高差int id=1;     for(int i=1;i<=n;i++) {        if(a[i]-a[id]>mina)          {              i--;              v[i]=true;              id=i;          }    }//求字典序    int head=1,tail=n;  for(int i=1;i<=n;i++)  {  if(!v[i]) ans[head++]=a[i];  else ans[tail--]=a[i];  } //输出答案  for(int i=1;i<=n;i++) cout<

分+贪心正解:

    这种做法似乎比较玄学而且看似不靠谱,然而AC的代码是最好的证据。感谢zyc同学提供的思路,本来差点失传了的思路,不过后来他get到了他以前的总结。点个大大的赞哦。


1、二分答案(身高差):

     很容易想到枚举身高差,在枚举的基础上优化,自然是二分。

     二分一个身高差,答案具有单调性,如果说身高差为x是可以的,那么x+1……oo都是可以的,同理,如果身高差x不可以,x-1……0更加是不可以的。所以,就可以得出一个身高差,用贪心来判断其合法性以及求出字典序。


2、贪心判断合法性:

    举个例子,同样是2 5 7 9 1519,假设此时二分到的答案是8,那么就找一个符合身高差(8)的最大的数才接到2的后面,显然这个数是9,然后把中间那些小的,5、7,挑处来。

    这样就组成了一个合法的上升序列。而挑出来的那些,组成一个下降序列,同时再比较一下两两之间是否会超过身高差。

    这样做成山坡状,满足一个圆环。

    这个例子是不合法的,因为先得出的上坡序列是2 9 15 19,接着在后面接上7 5,最后得出是2 9 15 19 7 5,但是通过后面往前的比较会发现19和7之间的差超过了8。说明二分出来的答案不合法。

    下面模拟一下合法答案(10)的过程。

    首先,一直到9-2,都是满足10的,所以把5和7挑出来。此时的序列为(2 9);接着9~19的身高差也是满足10的,再把15挑出来。最后把挑出来的组成下降序列,变成了15 7 5,接上前面的2 9 19,最后得出2 9 19 15 7 5,算两两之间的身高差,正好为10。

    但要注意的是,这样得出的序列并不是答案,因为在字典序上来说并不是最优的。

 

3、贪心算字典序:

    首先记下一个第一个用来比较的点,肯定是a[1]啦。然后用后面的数与其进行比较,如果不超过,tail++。于是,此时的最后一个tail,就是最大的合法值,也就是与a[1]身高差合法的最大的数,也是把它标记为1。

    其实这种字典序的做法和上面那种是雷同的,道理都是一样的。

#include #include #include #include using namespace std;const int INF=0x3FFFFFFF;bool vis[60];int down[60];int cur;int h[60];int main(){    freopen("1864.in","r",stdin);    freopen("1864.out","w",stdout);int t;    cin>>t;    while (t--)    {    int n;        cin>>n;        for (int i=1; i<=n; i++) cin>>h[i];        sort(h+1,h+n+1);        h[n+1]=INF;                int L=0,R=h[n]-h[1];//二分最大差值         while (L+1mid) p=0;                    break;                }                if (p) R=mid;                else L=mid;            }//维护一下,看看合不合法。         }        for (int i=1; i<=n; i++) vis[i]=0;        cur=1,down[1]=1;        int tail=2;        while (tail0; i--)if (vis[i]) cout<

    感谢zyc的代码资源。


纯DP正解:

    关于DP的做法可以参考(感觉我不是很理解):

    http://www.cnblogs.com/lsdsjy/p/4007063.html

1、用DP求最小的最大身高差:

    这个做法不仅在网上看到,lgj也给我们讲解过。因为我们要连成一个环。同样是上面的例子,2 5 7 9 15 19。

    最大的是19,于是构成一个环的起始点,可以看作是从大到小一个一个放的,传说中的双线DP。


    f[i][j]代表的意义为最左边的是第i个数,最右边的是第j个数,的最小的最大身高差。

    如果要放在左边,那么就要枚举右边的最后一个是什么(k),并且k是i-2~1中的数,那么身高差就是a[i-1]-a[i],与其子问题的差距进行比较,为两者取一个max;如果放右边,那身高差就是a[k]-a[i],与其子问题进行比较,为两者取一个max;在那么多max里面取一个min,就是此时的方案。

    细节留给读者自己思考。


2、用DP求字典序

    对的很暴力。和前一个DP差不多。先把最小值拿出来,然后枚举右边放哪个,当然啦,从前往后枚举,每次都跑一个算最小的最大身高差的DP,如果跑出来的值为最小的最大身高差(即上面求身高差DP得出的值),那说明这么放是可以的。(感觉很暴力的样子有木有)。


时间复杂度:

    O(N^4)

    实际运行起来是没有那么大的。


纯网络流算法:

   由于这种算法很迷,构图未果,留坑待填。




原创粉丝点击