题目链接:点击查看
题目大意:现在有 n 个队伍参加比赛,任意两个队伍之间都要进行一次比赛,也就是共需要进行 n * ( n - 1 ) / 2 次比赛,对于每个队伍来说,必须要在第一场比赛的时候到达赛场,在最后一场比赛结束后离开赛场,在赛场上呆的时间即为贡献,现在求出一种比赛的安排顺序,使得每个队伍的贡献之和最小
题目分析:可以自己手玩一下找找规律,这里以 n = 6 为例,画个图:
上图中表示了 n * ( n - 1 ) / 2 场比赛按照升序排列后,也就是按照红色箭头的方向依次比赛,相信肯定有不少同学在看完样例后以为这样是最优的,于是莽了一发,结果得到的是答案错误吧
我来一步一步优化一下整体序列,使得每次都变的最优吧,首先求一下当前情况下,每个队伍需要在赛场上滞留的天数
接下来我们不难发现,如果尝试将 ( 2 , 3 ) 这个点移到 ( 1 , 3 ) 之后,可以使得队伍 1 的贡献加一,队伍 2 和队伍 3 的贡献不变,队伍 4 , 5 , 6 的贡献减一,显然这样是更优的,于是我们移动一下
在此基础上,我们发现前移 ( 2 , 4 ) 也是可以让贡献减少,于是再次更新顺序
到此为止,可以得到当 n = 6 的答案序列了,是不是没有看出任何规律?好,那我们继续将 ( 2 , 5 ) , ( 2 , 6 ) 和 ( 3 , 4 ) 分别移动到相应的位置,看看结果会发生什么样的变化
到此为止,差不多就可以稍微总结一下结论或者规律了, 首先默认初始时比赛的顺序为最初的升序排列,对于一比赛不妨设为 ( i , j ) 满足 i < j ,如果将 ( i , j ) 前移(先不要管将其移动到什么位置),则对整体的贡献就是,i 前面的队伍,贡献会 +1 ,j 后面的队伍,贡献会 -1,用公式表达的话,( i , j ) 前移的贡献就是 sum += ( i - 1 ) - ( n - j ) ,也就是说,对于一场比赛我们可以分为三种情况:
- ( i - 1 ) - ( n - j ) < 0:这场比赛放在前面最优
- ( i - 1 ) - ( n - j ) == 0:这场比赛放在中间最优
- ( i - 1 ) - ( n - j ) > 0:这场比赛放在后面最优
推广一下发现这个结论对所有的 ( i , j ) 都适用,剩下的就是在三个部分中,各自排序的问题了,在上面手动模拟后,看的出中间部分和后面部分按照升序排列就好了,而前半部分需要按照 j 的升序排列,当 j 相同时再按照 i 的升序排列
证明的话我也不会,毕竟是比赛时找规律乱搞出来的,不过看完之后应该感觉还是比较有道理是吧?
实现就比较简单了
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=310;
bool maze[N][N];
void init(int n)
{
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
maze[i][j]=true;
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int w;
cin>>w;
while(w--)
{
int n;
scanf("%d",&n);
init(n);
vector<pair<int,int>>ans;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
if(i-1<n-j)
{
ans.emplace_back(i,j);
maze[i][j]=false;
}
}
sort(ans.begin(),ans.end(),[&](pair<int,int>a,pair<int,int>b)
{
if(a.second!=b.second)
return a.second<b.second;
return a.first<b.first;
});
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(maze[i][j])
ans.emplace_back(i,j);
for(auto it:ans)
printf("%d %d\n",it.first,it.second);
}
return 0;
}