CF# Wunder Fund Round 2016 (Div. 1 + Div. 2 combined) C计算几何 + D 图论

C题这比赛A,B就不用多讲了,没什么意义。 C题其实也不用讲,毕竟水题。
我的做法: 一开始以为是凸包,想去极角排序,排着排着就发现根本用不着极角0排啊!我们将所有的点按y从小到大排序,y相同的话就按x小的排。 按这个排序,把所有的点放到一个数组里面。

然后我们选定1,和2这两个点,然后我们只要从数组中往后扫符合条件的第一个就行了(扫描条件只有一个:那就是和1.2不会形成三点共线的情况)。 这个推理可以在纸上写一下,肯定(讲道理)是正确的。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<map>
#include<vector>
#define mem(a) memset(a,0,sizeof(a))
#define pfn printf("\n")
#define sf  scanf
#define pf  printf
#define fr(i,n) for(int i=0;i<n;i++)
#define INF 0x7fffffff   //INT_MAX
#define inf 0x3f3f3f3f   //
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
using namespace std;
#define lson l , mid , rt << 1
#define rson mid + 1 , r , rt << 1 | 1
const int MAXN=100005;
struct point
{
    __int64 x,y;
    __int64 num;
};
point list[MAXN];
__int64 stack[MAXN],top;
__int64 n;
__int64 cross(point p0,point p1,point p2){   //若<0 p2在p1下面,>0 p2在p1上,=0 共线,
    return (__int64)(p1.x-p0.x)*(p2.y-p0.y)-(__int64)(p1.y-p0.y)*(p2.x-p0.x);
}
double dis(point p1,point p2) {          //计算 p1p2的 距离
    return sqrt((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
bool cmp(point p1,point p2){           
    if(p1.y<p2.y) return true;
    else if(p1.y==p2.y && p1.x<p2.x) return true;
    else return false;
}
void init(int n){    //输入,并把  最左下方的点放在 list[0]  。并且进行排序
    __int64 i,k;
    point p0;
    scanf("%I64d %I64d",&list[0].x,&list[0].y);
    list[0].num=1;
    p0.x=list[0].x;
    p0.y=list[0].y;
    p0.num=list[0].num;
    k=0;
    for(i=1;i<n;i++){
        scanf("%I64d %I64d",&list[i].x,&list[i].y);
        list[i].num=i+1;
        if( (p0.y>list[i].y) || ((p0.y==list[i].y)&&(p0.x>list[i].x)) ){
            p0.x=list[i].x;
            p0.y=list[i].y;
            p0.num=list[i].num;
            k=i;
            list[k]=list[0];
            list[0]=p0;
        }
    }
    sort(list+1,list+n,cmp);   //list[0] 不变
}

void graham(int n){
    __int64 i=2;
    while(cross(list[0],list[1],list[i])==0 ) {
        i++;
    }
    printf("%I64d %I64d %I64d\n",list[0].num,list[1].num,list[i].num);
}
int main(){
    //freopen("1.txt","r",stdin);
    while(~scanf("%d",&n)){
        if(n==3){
            printf("1 2 3\n");
            break;
        }
       init(n);
       graham(n);
    }
    return 0;
}

D
题意:
就是说有一个完全图,有n个点,每一条路花费y秒,输入会给出n-1条边(一颗生成树),这些边将会花费x秒,注意:x不一定小于y。 问:每一个点都要走一遍,而且只能走一遍,花费最少的时间是多少?

题目咋一看是很难的,因为一开始想的是每一点都只能走一遍,然后就是要找欧拉路径,然后又要尽量多的包含min(x,y)路径,一顿想,然后……..感觉这个不在我的能力范围了。

但其实题目中往往一两个条件(你忽略的) 才是至关重要的! 你看:
1. 题目中给出边数量:(n-1)条边
2. 这是一个完全图,任何两点之间都有边
由此可知:欧拉路径根本不用找,因为完全图肯定存在无数条路径,我们需要做的只是选择尽量多x路径
若x>=y
最终结果 只有两个:1.(n-2)y+x 2.(n-1)*y
1可能存在某个点连接了n-1条边 2. 完全图,随便走
若 x< y
其实无论一个点连了多少条x边,实际上这个点对于整个路径的贡献度最多就只有2,而这个点下面的其他点各自的贡献度就可以用dfs解决。
在dfs里面 对于A–B–C ,对a点的时候,a的贡献度只由连接A 的边决定,我们递归到B的时候,这个时候连接B的边 就已经与A 无关了,这个也要注意下。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<map>
#include<vector>
#define mem(a) memset(a,0,sizeof(a))
#define pfn printf("\n")
#define sf  scanf
#define pf  printf
#define fr(i,n) for(int i=0;i<n;i++)
#define INF 0x7fffffff   //INT_MAX
#define inf 0x3f3f3f3f   //
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
using namespace std;
#define lson l , mid , rt << 1
#define rson mid + 1 , r , rt << 1 | 1
int n,x,y,i,j,k;
vector<int> g[200200];
__int64 ans;  //ans表示可以走x的路径条数
int dfs(int i, int p) { 
  int len=0;
  for (int j=0; j<g[i].size(); j++) {
    int k=g[i][j];
    if (k==p)
        continue;
    len+=dfs(k,i); //从i点走到j点,i
  }
  if (len>=2) {
    ans+=2;   //从A--B---C 两条x路,纵使B还可以---D,但是选了C,就不能够回到B了
    return 0; //表示把B的贡献度直接+进ans,与上一层无关,返回0可以处理这个问题
  }
  ans+=len; //但若是len=1,表示A(起点)--B--C而没有D ,那完全可以把起点A换成C,这样x就多了1条
  return 1;// + i->j这一条路
}
int main() {
 // freopen("1.txt","r",stdin);
  while(~scanf("%d%d%d",&n,&x,&y)){
      for (i=1; i<n; i++) {
        scanf("%d%d",&j,&k);
        g[j].push_back(k);
        g[k].push_back(j);
      }
      for (i=1; i<=n; i++)
          if (g[i].size()==n-1)
              break;
      if (i<=n) {  //因为一共就只有n-1路,所以若加在一条路上,这肯定只能走1个x
        printf("%I64d\n",(__int64)y*(n-2)+x);
        return 0;
      }
      if (x>=y) { //如果x>y,而且有又上一个条件的筛选,不会把一个点封死
        printf("%I64d\n",(__int64)y*(n-1));
        return 0;
      }
      dfs(1,0); //随便从哪个点出发,就从1出发
      printf("%I64d\n",ans*x+(n-1-ans)*y);   //
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值