ZOJ Problem Set - 1239 (最小点覆盖 )

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=239

337这个题普通的方法或打表就可以过,在这里主要是用最小路径覆盖思想解一下。这题需要转化一下,将问题从求m根柱子最多能放多少球,变为,n个球最少 需要多少柱子。如果对i<j,i + j 为完全平方数,则有一条从i到j的有向边,就变成了求图的最小路径覆盖。这时候只要在预处理中把最小路径覆盖小于50的全部算出来就可以了。

求最 小路径覆盖一般是转化为求图的二分匹配,但是要对图进行一些转化,构造一个新图。将节点i分裂为两个节点i, i',如果原图中有从i到j的节点,则新图中有从i到j'的边,其实就是将一个点分为入点、出点,去除掉同一路径上不可以同时匹配相邻节点的限制,保证二 分匹配和最小路径覆盖一一对应,此时,满足公式:原图最小路径覆盖 = 原图顶点数 - 最大二分匹配。


  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cmath>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include< set>
  7 #include<map>
  8 #include<queue>
  9 #include<vector>
 10 #include< string>
 11  #define Min(a,b) a<b?a:b
 12  #define Max(a,b) a>b?a:b
 13  #define CL(a,num) memset(a,num,sizeof(a));
 14  #define eps  1e-6
 15  #define inf 10001000
 16 
 17  #define ll   __int64
 18 
 19  #define  read()  freopen("data.txt","r",stdin) ;
 20  const  double pi  = acos(- 1.0);
 21  const  int maxn =  2000;
 22 
 23  using  namespace std;
 24  int n,m;
 25  int head[maxn] ;
 26  int result[maxn],vis[maxn] ;
 27  struct node
 28 {
 29      int v;
 30      int next;
 31 }p[maxn*maxn];
 32  int mat[maxn][maxn] ;
 33  int cnt,num ;
 34  int f[maxn* 2];
 35  int ans[ 55] ;
 36  void add( int u, int v)
 37 {
 38     p[cnt].v = v;
 39     p[cnt].next = head[u];
 40     head[u] = cnt++ ;
 41 }
 42  bool find( int u)
 43 {
 44 
 45      for( int i =  1;i <= num;i++)
 46     {
 47 
 48          if(mat[u][i]&&!vis[i])
 49         {
 50             vis[i] =  1 ;
 51              if(result[i] == - 1||find(result[i]))
 52             {
 53                 result[i] = u;
 54                  return  true;
 55             }
 56         }
 57     }
 58      return  false ;
 59 }
 60  int  get()
 61 {
 62 
 63      int ans=  0;
 64     CL(result,- 1);
 65      for( int i =  1;i <= num;i++)
 66     {
 67         CL(vis, 0);
 68          if(find(i))ans++;
 69     }
 70      return ans ;
 71 }
 72  void init()
 73 {
 74      int i,j;
 75     CL(f, 0) ;
 76     CL(mat, 0) ;
 77      // CL(head,-1) ;
 78      cnt =  0;
 79 
 80      for(i =  1 ;i*i < maxn* 2;i++)
 81     {
 82          f[i*i] =  1;
 83     }
 84      for(i =  1;i<maxn;i++)
 85     {
 86          for(j = i +  1;j<maxn;j++)
 87         {
 88              if(f[i+j] == 1)mat[i][j] =  1;
 89         }
 90     }
 91 
 92      for(i =  1;i< maxn;i++)
 93     {
 94         num = i;
 95          int k =  get();
 96          if(i - k >  50) break;
 97         ans[i - k] = i;
 98     }
 99 
100 
101 }
102  int main()
103 {
104      int t;
105     init() ;
106     scanf( " %d ",&t);
107      while(t--)
108     {
109         scanf( " %d ",&n);
110         printf( " %d\n ",ans[n]) ;
111     }
112 }

转载于:https://www.cnblogs.com/acSzz/archive/2012/10/12/2721618.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值