Codeforces 164 D Minimum Diameter

题目链接~~>

做题感悟:越来越感觉CF的题非常好,非常有深度。

解题思路:

                 这题须要注意 k 的大小。由于 k 仅仅有 30 个,终于形成的点的直径一定是某个确定的值,所以我们能够枚举这个值。然后把大于这个值的边都取消(删除不大于k 个点),这样不断二分剩下点的最小直径。每次二分的时候推断是否合法。

这里推断是否合法非常重要。由于这须要用dfs去枚举,一不小心就会超时。dfs 在枚举删除每一个点的时候主要枚举两个方面。(1)假设想删除与当前点相连的全部边,能够选择删除全部与之相连的点 (2)能够单独删除此点,然后与之相连的全部边都删除了。这里有一个剪枝:假设以下删除所达到的状态不如上面的优就不继续深搜下去了。由于到达当前点所形成的状态是一样的(都把与1 ~ x 节点相连的边都删除了),就看剩下能够删除点多的必然优。

代码:

#include<iostream>
#include<sstream>
#include<map>
#include<cmath>
#include<fstream>
#include<queue>
#include<vector>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<stack>
#include<bitset>
#include<ctime>
#include<string>
#include<cctype>
#include<iomanip>
#include<algorithm>
using namespace std  ;
#define INT __int64
#define L(x)  (x * 2)
#define R(x)  (x * 2 + 1)
const int INF = 0x3f3f3f3f ;
const double esp = 0.0000000001 ;
const double PI = acos(-1.0) ;
const int mod = 1e9 + 7 ;
const int MY = 1400 + 5 ;
const int MX = 1010 + 5 ;
int num ,n ,k ;
vector<int>G[MX] ;
int d[MX*MX] ,g[MX][MX] ,vis[MX] ;
struct node
{
    int x ,y ;
}P[MX] ;
bool dfs(int cnt ,int m) // cnt 代表编号 。m 代表删除的边数
{
    if(cnt > n)     return true ;
    if(vis[cnt])    return dfs(cnt + 1 ,m) ;
    for(int i = 0 ;i < (int)G[cnt].size() ; ++i)
    {
         m += !vis[G[cnt][i]] ;
         vis[G[cnt][i]]++ ;
    }
    if(m <= k && dfs(cnt + 1 ,m))
               return true ;
    int temp = m ;
    for(int i = 0 ;i < (int)G[cnt].size() ; ++i)
    {
        vis[G[cnt][i]]-- ;
        m -= !vis[G[cnt][i]] ;
    }
    if(G[cnt].size() != 1)
    {
        vis[cnt]++ ;
        if(m + 1 <= k && m + 1 < temp && dfs(cnt+1 ,m+1))
            return  true ;
        vis[cnt]-- ;
    }
    return false ;
}
bool judge(int dist)
{
    memset(vis ,false ,sizeof(vis)) ;
    for(int i = 1 ;i <= n ; ++i)
         G[i].clear() ;
    for(int i = 1 ;i < n ; ++i)
      for(int j = i+1 ;j <= n ; ++j)
          if(g[i][j] > dist)
          {
              G[i].push_back(j) ;
              G[j].push_back(i) ;
          }
    return  dfs(1 ,0) ;
}
int binary_search(int le ,int rt)  //
{
    int mid ;
    while(le < rt)
    {
       mid = (le + rt)>>1 ;
       if(judge(d[mid]))  rt = mid ;
       else  le = mid + 1 ;
    }
    return le ;
}
int main()
{
    //freopen("input.txt" ,"r" ,stdin) ;
    while(~scanf("%d%d" ,&n ,&k))
    {
        num = 0 ; d[0] = 0 ;
        for(int i = 1 ;i <= n ; ++i)
           scanf("%d%d" ,&P[i].x ,&P[i].y) ;
        for(int i = 1 ;i < n ; ++i)
          for(int j = i+1 ;j <= n ; ++j)
            d[++num] = g[i][j] = (P[i].x-P[j].x)*(P[i].x-P[j].x) + (P[i].y - P[j].y)*(P[i].y-P[j].y) ;
        sort(d ,d + num + 1) ;
        num = unique(d ,d+num+1) - d - 1 ;
        int mx = binary_search(0 ,num) ; //   二分查找最小直径
        judge(d[mx]) ;
        bool first = false ;
        for(int i = n ;i >= 1 ; --i)
          if(vis[i])
          {
              if(first)  putchar(' ') ;
              printf("%d" ,i) ;
              k-- ;
              first = true ;
          }
        for(int i = n ;i >= 1 && k > 0 ; --i)
           if(!vis[i])
           {
               if(first)  putchar(' ') ;
               printf("%d" ,i) ;
               k-- ;
               first = true ;
           }
        cout<<endl ;
    }
    return 0 ;
}



posted @ 2017-06-15 21:31  yfceshi  阅读(245)  评论(0编辑  收藏  举报