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;
}