1. 程式人生 > >Google Code Jam 2015 Round 1A 解題報告

Google Code Jam 2015 Round 1A 解題報告

        太弱了,比賽時只出了A的大資料和BC小資料,排到1700+。賽後才把B的小資料過了。

A. Mushroom Monster

        就是貪心模擬。第一種吃法,只有當前時刻比10秒前少才去吃減少的量,否則不吃。第二種吃法,計算每十秒的減少量,以減少量最大的作為平均速率去吃。

#include <bits/stdc++.h>      
using namespace std;  

int m[10010];

int main(){
	freopen("A-large.in","r",stdin);
	freopen("A-large.out","w",stdout);
	int t;
	cin>>t;
	int x=0;
	while(t--){
		x++;
		int n;
		cin>>n;
		for(int i=1;i<=n;i++){
			scanf("%d",&m[i]);
		}
		int y=0;
		int MAX_speed=0;
		for(int i=2;i<=n;i++){
			if(m[i]<m[i-1]){
				y+=m[i-1]-m[i];
				if(m[i-1]-m[i]>MAX_speed){
					MAX_speed=m[i-1]-m[i];
				}
			}
		}
		int z=0;
		
		for(int i=1;i<n;i++){
			if(m[i]<MAX_speed){
				z+=m[i];
			}else{
				z+=MAX_speed;
			}
		}
		printf("Case #%d: %d %d\n",x,y,z);
	}
	return 0;
}


B. Haircut

        建立一個優先佇列去模擬,佇列大小是理髮師人數,每次有人理完髮(pop),下一個人接著上(push)。解題關鍵在減小模擬的量,因為模擬n個人會超時。小資料可以求時間的公倍數,計算出公倍數時間能理的人數,用總人數模它。大資料則需要二分找一個接近理完但是沒有理完的時間,從那個時間開始模擬。

#include <bits/stdc++.h>      
using namespace std;  

#define ll long long

int m[1010];

struct node{
	int idx;
	ll tot_time;
	node(int idx,ll tot_time):idx(idx),tot_time(tot_time){
	}
	node(){
	}
	bool operator <(const node& other)const{
		if(tot_time!=other.tot_time){
			return tot_time>other.tot_time;
		}
		return idx>other.idx;
	}
};

int main(){
	freopen("B-large-practice.in","r",stdin);
	freopen("B-large-practice.out","w",stdout);
	int t;
	cin>>t;
	int cas=0;
	while(t--){
		cas++;
		int b,n;
		cin>>b>>n;
		priority_queue<node> que;
		
		ll MAX_time=0;
		ll MIN_time=100000;
		for(int i=1;i<=b;i++){
			scanf("%d",&m[i]);
			if(m[i]>MAX_time)MAX_time=m[i];
			if(m[i]<MIN_time)MIN_time=m[i];
		}
		
		ll l=(n*MIN_time)/b-MAX_time;
		ll r=(n*MAX_time)/b+MAX_time;
		ll aTime=l;
		while(l<=r){
			ll mid=(l+r)>>1;
			ll cnt=0;
			for(int i=1;i<=b;i++){
				cnt+=mid/m[i];
				if(mid%m[i])cnt++;
				if(cnt>=n)break;
			}
			if(cnt>=n){
				r=mid-1;
			}else{
				l=mid+1;
				aTime=mid;
			}
		}
		
		ll cnt=0;
		for(int i=1;i<=b;i++){
			cnt+=aTime/m[i];
			if(aTime%m[i])cnt++;
			que.push( node(i, (m[i]-aTime%m[i])%m[i] ) );
		}
		
		for(int i=cnt+1;i<n;i++){
			node cur=que.top();	que.pop();
			que.push( node( cur.idx,cur.tot_time+m[cur.idx]) );
		}
		node ans=que.top();
		printf("Case #%d: %d\n",cas,ans.idx);
	}
	return 0;
}


C. Logging

        一看到凸包有點蒙了,不過解法貌似不需要凸包。小資料可以列舉每個點i,然後再列舉每個不同於i的點j,連線ij,用叉積計算直線兩側有多少點,取較小的那一側,對所有j取最小值。這個結果就是為了使i在凸包上需要去掉的點數。大資料不會做。。。

#include <bits/stdc++.h>      
using namespace std;  

#define ll long long

struct Point{
	ll x,y;
	Point(ll x,ll y):x(x),y(y){
	}
	Point(){
	}
}pts[3010];

struct Vector{
	ll x,y;
	Vector(ll x,ll y):x(x),y(y){
	}
	Vector(){
	}
	Vector(Point A,Point B){
		x=B.x-A.x;
		y=B.y-A.y;
	}
};

ll Cross(Vector A,Vector B){
	return A.x*B.y - A.y*B.x;
}

int main(){
	freopen("C-small-attempt2.in","r",stdin);
	freopen("C-small-attempt2.out","w",stdout);
	int t;
	cin>>t;
	int cas=0;
	while(t--){
		cas++;
		int n;
		cin>>n;
		
		for(int i=1;i<=n;i++){
			cin>>pts[i].x>>pts[i].y;
		}
		printf("Case #%d:\n",cas);
		
		for(int i=1;i<=n;i++){
			int ans=max(n-3,0);
			for(int j=1;j<=n;j++){	//列舉 
				if(i==j)continue;
				int left=0;
				int right=0;
				for(int k=1;k<=n;k++){
					if(k==i)continue;
					if(k==j)continue;
					Vector v1 = Vector(pts[i],pts[j]);
					Vector v2 = Vector(pts[i],pts[k]);
					ll cp = Cross(v1,v2);
					if(cp<0)left++;
					if(cp>0)right++;
				}
				int tmp=min(left,right);
				ans=min(ans,tmp);
			}
			cout<<ans<<endl;
		}
	}
	return 0;
}