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 ;
}
时间: 2024-08-30 18:35:08

Codeforces 164 D Minimum Diameter的相关文章

【Codeforces 1086B】Minimum Diameter Tree

[链接] 我是链接,点我呀:) [题意] 题意 [题解] 统计叶子节点个数m 把每条和叶子节点相邻的边权设置成s/cnt就可以了 这样答案就是2*s/m(直径最后肯定是从一个叶子节点开始,到另外一个叶子节点结束) 证明: 设dis(i,j)表示节点i和节点j之间的权值和 设a[1],a[2]..a[m]是m个叶子节点 则 \(max(dis(a[i],a[j])) >= ∑\frac{dis(a[i],a[j]) }{ \frac{m*(m-1)}{2|} }\) 即\(\frac{m*(m-1

Codeforces 730B:Minimum and Maximum(交互式问题)

http://codeforces.com/problemset/problem/730/B 题意:一个交互式问题,给出一个n代表有n个数字,你可以问下标为x和y的数的大小,会给出">","<"或"=",要求询问次数不能超过 ,最后输出最小的数和最大的数的下标. 思路:很新奇的题目.先将两两比较整理出一个max数组和min数组,这个时候前后较大的一个会在max数组中,较小的会在min数组中,这个时候询问了n/2次.接着我们只要对每个组

Codeforces 1082 D. Maximum Diameter Graph-树的直径-最长链-构造题 (Educational Codeforces Round 55 (Rated for Div. 2))

D. Maximum Diameter Graph time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Graph constructive problems are back! This time the graph you are asked to build should match the following proper

Codeforces 164 E Compatible Numbers

主题链接~~> 做题情绪:好题,做拉的比赛的时候想了非常久,想到枚举变幻某一位的 0 为 1 .可是每一个数都这样枚举岂不超时的节奏,当时没想到事实上从大到小枚举一次就 ok 了. 解题思路: 本题要求两个数  a & b = 0 , 假设 a  =  10010 , b 至少(指在 a 中的为 1 的位必须为 0 )是 01101 ,还能够是 00101 ,00001 .00000.就相当于你去买东西一样,先提出你的要求(必须满足).至于其它方面都无所谓. 这样我们能够枚举 b 中的 1

CodeForces 279D The Minimum Number of Variables 题解

题目大意: 有一组n个不相同的数字组成数串:a1,a2,a3-an. 1.一个数组b. 2.第一个操作我们将b0的值赋为a1.之后我们有n-1个操作,第k次操作我们将by=bi+bj(y,i,j可能相同). 3.每次操作结束后我们依次取出by.按顺序组成新串. 问操作结束后,我们获得的新串能否与a数串相同.如果可以输出数组b所需的最小长度.反之输出-1. 思路: 先介绍一个纯dfs的算法:每次依次枚举i,j,y,然而会T. 不过,可以发现,b中的数必定在a中出现过.因此,记忆化搜索,状态(s)中

Codeforces 805 D Minimum number of steps

题意: 给定一串字符串,将所有"ab"的子串替换为"bba",询问多少次操作后没有子串"ab". 分析: 观察可得,将"ab"替换为"bba"有两种结果. ①a移到了b的后面 ②增加了一个b 而且最终的结果一定是前面全是b,后面全是a. 所以可以猜想从后往前数,设置一个B_cnt, 每当碰到一个b, 就b_cnt++, 碰到A, 就先加上一个b_cnt(因为每替换一次会将a移动后一格,所以要替换b_cnt次

【转化为二分图判定问题的难题】

关键词:并查集,二分图,搜索. 例题一: CodeForces-85E:Guard Towers 题意:给定平面上N个点(N<=5000),以及N个点的坐标.现在可以把每个点染成红色或者蓝色.求最小化同色点的最大距离,且求出相应的方案数. 思路:二分答案L,把距离大于等于L的连边,然后判定是否是二分图. 对于求方案数,这个先不管它. (当然,最优的解法是转化为切比雪夫距离,复杂度极低,这个也先不管它.) 例题二: CodeForces-164D:Minimum Diameter 题意:给定平面上

Minimum Integer CodeForces - 1101A (思维+公式)

You are given qq queries in the following form: Given three integers lili, riri and didi, find minimum positive integer xixi such that it is divisible by didi and it does not belong to the segment [li,ri][li,ri]. Can you answer all the queries? Recal

codeforces水题100道 第十六题 Codeforces Round #164 (Div. 2) A. Games (brute force)

题目链接:http://www.codeforces.com/problemset/problem/268/A题意:足球比赛中如果主场球队的主场球衣和客队的客队球衣颜色一样,那么要求主队穿上他们的可对球衣,现在有n个球队,每一球队的主场球衣和客场球衣的颜色都告诉你了,它们两两之间要比一场赛.求其中主队穿客场球衣的比赛场数.C++代码: #include <iostream> using namespace std; const int maxn = 33; int x[maxn], y[max