hdu3335 有向图最少路径覆盖

来源:互联网 发布:java如何控制并发 编辑:程序博客网 时间:2024/05/27 21:06

解题报告

题目 http://acm.hdu.edu.cn/showproblem.php?pid=3335

题目大意 :给出N1000)个数字(<2^63 -1),从n中取出k个数,使得任意ab属于kab不形成整除关系。问k最大是多少

算法 :有向图最小路径覆盖数

思路 :因为要求取出一些点,使得他们之间没有整除关系,很容易想到利用整除关系建立一个图,然后看最多有多少个点能不相连,如果把图看作无向图,那么就很难想到做法,至少无向图最大点独立集是不能在1000的规模下运行的。如果ab的约束,我们建立一条ab的有向边,最后发现,要求的其实就是最小路径覆盖数。

最小路径覆盖数:在一个有向图中至少放几个人才能走到所有点(延边走,人可以放在任意位置,非官方解释)。

最小路径覆盖 :在一个有向图中,最少用几条路径能把所有的点覆盖(路径不交叉,即每个点只属于一条路)。

当有向图无环时,可以用二分图最大匹配来求解。

最少路径覆盖 =点数 二分图最大匹配

其中二分图中的点是有原图中的点拆点后形成,一定要注意下面两点:

1:若我们求的是路径不能交叉的最小路径覆盖,则直接二分图就行。

2:若我们求的是路径能够交叉的最小路径覆盖,组应该先进行floyed,然后再二分图。

假设从在边(a,b(b,c), (d,b), (b,e)那么Floyed完以后相当于是又从ace另加了边来绕过原来路径中交叉的那个b点,也就是转化成了不能路径交叉的最小路径覆盖。

再回头来看我们这个题。题目要求是求最多的数,按照题目要求,在我们所建立的图里,一条路径代表的是一条约数路,也就是这一条路上我们只能最多拿出一个点,而最小路径覆盖便是所有的这样的边的个数。

提交情况 wrong answer5次(题目中有重复数字,须去重)。

                     Accepted 1

经验与收获 :曾经的例题,比赛中却没看出来,知识掌握的很不牢固啊。

ACcode

#include <stdio.h>

#include <string.h>

#include <algorithm>

using namespacestd;

 

#define MAXN 1010

typedef __int64I64;

 

struct EDGE{

   int to, next;

}edges[MAXN * MAXN];

inthead[MAXN], ad, vis[MAXN], match[MAXN];

I64 a[MAXN];

 

voidclear(){

   ad = 0;

   memset(head, - 1, sizeof(head));

}

 

void insert(int x,int y){

   edges[ad].to = y; edges[ad].next = head[x]; head[x] = ad++;

}

 

int find(intu){

   int i;

   for(i = head[u]; ~i; i =edges[i].next){

      int v = edges[i].to;

      if(!vis[v]){

          vis[v] = 1;

          if(match[v] == -1 ||find(match[v])){

             match[v] = u;

             return 1;

          }

      }

   }

   return 0;

}

 

int main(){

   int CASE;

   int n, i, j;

   scanf("%d",&CASE);

   while(CASE --){

      scanf("%d",&n);

      for(i = 0; i < n; i++)

          scanf("%I64d",&a[i]);

      clear();

      int m = 0;

      for(i = 0; i < n; i++)

          if(a[i])

             for(j = 0; j < n; j++)

                 if(i != j&& a[j] == a[i]){

                    a[j] = 0;

                    m ++;

                 }

                 else

                    if(a[j]&& i != j&& a[j] % a[i] == 0) insert(i,j);

      int ans = 0;

      memset(match, -1, sizeof(match));

      for(i = 0; i < n; i++){

          memset(vis, 0, sizeof(vis));

          if(find(i)) ans ++;

      }

      printf("%d\n", n - ans -m);

   }

   return 0;

}