解题报告:Codeforces Round #432 (Div. 1) D. Tournament Construction (DP+构造)

题目链接

题意:

给出点的出度的去重集合,要求构造一个最小点数的竞赛图

并存在一个出度序列(d1,d2,d3...dn)满足任意前缀k项和大于k*(k-1)/2

(点数<=30,0<=出度<=30)


思路:

可以确定点数的上界为61   ( n*30>=n*(n-1)/2 )

定义dp[ n ] [ m ] [ l ] :能否用集合的前m项(至少取一个)构造出n个点  l 条边的图

那么很容易得到转移方程:

找出满足要求的最小n,dfs出n个点的出度

最后一步是利用这个n个点构造出一个竞赛图

可以发现一个竞赛图删除一个点以及它的所有边后仍然是一个竞赛图

那么避免冲突每次选择最小出度的点更新边以及其他点的出度即可


代码:

#include<bits/stdc++.h>

using namespace std;

int n,m;
int A[35],ans[65],pos[65];
bool G[65][65],dp[65][35][2000];

void dfs(int a,int b,int c){
   if(!a)return ;
   ans[a--] = A[b];c-=A[b];
   if(dp[a][b][c])return dfs(a,b,c);
   return dfs(a,b-1,c);
}

inline void makeG(){
    for(int i=1;i<=n;++i)pos[i]=i;
    for(int i=1;i<=n;++i){
      sort(pos+i,pos+n+1,[](int a,int b){return ans[a]<ans[b];});
      for(int j=i+1;j<=i+ans[pos[i]];++j)G[pos[i]][pos[j]]=1;
      for(int j=i+ans[pos[i]]+1;j<=n;++j)G[pos[j]][pos[i]]=1,ans[pos[j]]--;
   }
}

int main()
{
   scanf("%d",&m);n = 1 ;
   for(int i=1;i<=m;++i)scanf("%d",&A[i]);
   sort(A+1,A+m+1);dp[1][1][A[1]] = true;
   while(n<62&&(n<m||!dp[n][m][n*(n-1)/2])){
      for(int i=1;i<=m;++i)
         for(int j=n*(n-1)/2,ed=n*A[m];j<=ed;++j)
            if(dp[n][i][j])dp[n+1][i][j+A[i]] = dp[n+1][i+1][j+A[i+1]] = true;
      ++n;
   }
   if(n>61){
      printf("=(\n");
      return 0;
   }
   dfs(n,m,n*(n-1)/2);
   printf("%d\n",n);makeG();
   for(int i=1;i<=n;++i,printf("\n"))for(int j=1;j<=n;++j)
      printf("%d",G[i][j]);
   return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值