SPOJ watermelon 问题过程记录【一】


274. Johnny and the Watermelon Plantation

Problem code: WMELON

Shortly after his abdication from the Bytelandian throne Johnny decided to go into farming. Water melons were a natural choice as his first crop ever, since they seemed easy enough to grow and look after. So, he sold all his beer bottles and for the money he purchased a 1km x 1km square field. Here it was that he planted the water melon seeds. (The word 'planted' is really a bit of a euphemism for walking across a field gorging on a water melon and spitting out the pips but, for the sake of politeness, let us leave it this way).

To everyone's surprise a lot of the seeds sprouted stems, and soon enough many of the plants showed signs of fruit (and some had even more than one!). Then quite unexpectedly, when the water melons were still a little too unripe to eat, winter set in. Johnny knows that he has to construct a green house to protect the field but, with his rather limited budget, he cannot afford the glass to cover the whole area. He has decided that it is enough that k fruit survive the ordeal under a glazed roof. For reasons of architectural planning in Byteland it is necessary that the green house be a rectangle with sides parallel to the edges of the plot.

You have been requested to help Johnny minimise investement costs. Since glass is paid for by the square meter, design a green house with the smallest possible area fulfilling the imposed conditions.

Input

The first line of input contains the integer t<=100, the number of test cases. t test cases follow.

Every test case begins with a line containing two integers n k, denoting the total number of plants and the number of water melon fruit to be protected, respectively (1<=n<=1000, 1<=k<=106, k doesn't exceed the total number of fruit in the plantation). Each of the next n lines describes a single plant, the i-th line containing three integers xi yi fi - the X and Y coordinates of the plant, and the number of water melon fruit on it, respectively (1<=xi, yi, fi<=1000).

Output

For each test case output a single integer, denoting the area of the smallest possible rectangular glass house with horizontal and vertical edges, sufficient to cover at least k fruit of the plantation.

Example

Input:
1
6 11
1 1 2
1 2 2
3 1 2
3 2 3
4 2 5
3 3 2

Output:
2

Illustration of sample test data


Added by:Adrian Kosowski
Date:2005-01-03
Time limit:17s
Source limit:50000B
Memory limit:1536MB
Cluster:Cube (Intel Pentium G860 3GHz)
Languages:All except: NODEJS PERL 6
Resource:DASM Programming League 2004, problemset 5 (acknowledgement to Thanh Vy Hua Le)

   

首先想到的是最简单的办法,就是从1,1点开始,从这个点往四个方向递归,从外上、外下、左外、右外分别删除一条边,然后找出最小的面积,逐层返回;根据我对题目的理解,当只有一个点就能满足情况时,面积应该为1,至今我还没在spoj上通过,所以也不知道答案是什么。。。。

然后第一版代码是这样的:

#include<stdio.h>
#include<iostream>
#include<vector>
#include<map>
using namespace std;
map<pair<int,int>,int> farm;
int casenumber=0;
int plant=0;
int w_protected=0;
int min_x=1000;
int max_x=0;
int min_y=1000;
int max_y=0;
void getInput(istream& cin);//输入数据
int minArea(int,int,int,int);//计算最小面积的主方法
bool isLegal(int,int,int,int,int,int);//检测该点是否在当前范围内
int min(int,int,int,int);//计算分别删除四条边得到结果中的最小值
void reset();//进行下一个case之前清空数据


void getInput(istream& cin){
	cout<<"请输入有多少个case:"<<endl;
	cin>>casenumber;
	int i=0;
	while(i<casenumber){
		cout<<"多少颗西瓜藤,保护多少个?"<<endl;
		cin>>plant;
		cin>>w_protected;
		int j=0;
		while(j<plant){
			cout<<"西瓜种植情况 x,y,个数表示"<<endl;
			int x=0;
			int y=0;
			int w_number=0;
			cin>>x;
			cin>>y;
			cin>>w_number;
			farm.insert(map<pair<int,int>,int>::value_type(pair<int,int>(x,y),w_number));
			if(x<min_x){
				min_x=x;
			}
			if(x>max_x){
				max_x=x;
			}
			if(y<min_y){
				min_y=y;
			}
			if(y>max_y){
				max_y=y;
			}
			j++;
		}
		int min=minArea(min_x,max_x,min_y,max_y);
		if(min==-1){
			cout<<"你确定你有那么多西瓜?"<<endl;
		}else{
			cout<<"你需要这么大面积的材料: "<<min<<endl;
		}
		i++;
		//完成一个case后需要重置变量
		reset();
	}	
}

void reset(){
	min_x=1000;
	max_x=0;
	min_y=1000;
	max_y=0;
	plant=0;
	w_protected=0;
	farm.clear();
}

int minArea(int x_min,int x_max,int y_min,int y_max){
	/*
	if(x_max==(x_min+1)&&y_max==(y_min+1)){
		return 1;
	}
	*/
	map<pair<int,int>,int>::iterator iter;
	int total=0;
	for(iter=farm.begin();iter!=farm.end();iter++){
		pair<int,int> pair=iter->first;
		int wm_number=iter->second;
		int x=pair.first;
		int y=pair.second;
		if(isLegal(x_min,x_max,y_min,y_max,x,y)){
			total=total+wm_number;
		}

	}
	//如果合法则说明能够进行下一轮删减
	if(total>=w_protected){
		int area_1=0;
		int area_2=0;
		int area_3=0;
		int area_4=0;
		if(x_max>(x_min+1)){
			area_1=minArea(x_min+1,x_max,y_min,y_max);
			area_2=minArea(x_min,x_max-1,y_min,y_max);
		}else{
			area_1=-1;
			area_2=-1;
		}
		if(y_max>(y_min+1)){
			area_3=minArea(x_min,x_max,y_min+1,y_max);
			area_4=minArea(x_min,x_max,y_min,y_max-1);
		}else{
			area_3=-1;
			area_4=-1;
		}
		int min_area=min(area_1,area_2,area_3,area_4);
		if(min_area!=-1){
			return min_area;
		}
		else{
			int height=y_max-y_min;
			int width=x_max-x_min;
			if(height==0){
				height=1;
			}if(width==0){
				width=1;
			}
			return height*width;
		}
	}else{
		return -1;
	}
}

bool isLegal(int x_min,int x_max,int y_min,int y_max,int number_x,int number_y){
	if(x_min<=number_x&&number_x<=x_max&&y_min<=number_y&&number_y<=y_max){
		return true;
	}
	return false;
}
int min(int x1,int x2,int x3,int x4){
	int min=1000000;
	if(x1!=-1){
		min=x1;
	}
	if(x2<min&&x2!=-1){
		min=x2;
	}
	if(x3<min&&x3!=-1){
		min=x3;
	}
	if(x4<min&&x4!=-1){
		min=x4;
	}
	//如果都不能达到要求,则返回-1
	if(x1==-1&&x2==-1&&x3==-1&&x4==-1){
		return -1;
	}else{
		return min;
	}
}

int main(){
	cout<<"开始输入:"<<endl;
	getInput(cin);

}

时间复杂度4^1000,跑

2 2

1 1 1

500 500 1

的时候跑了十分钟差不多都没跑出来。。。。写出这种代码也真是呵呵哒,还写了快三个小时吧


然后意识到这个问题,递归删边的时候有些隔了几条边都没有点,因此将递归的范围缩小到存在植株的点,每次删边也是删除到下一个存在的x或者y;因此第二版代码是这样的:

#include<stdio.h>
#include<iostream>
#include<vector>
#include<map>
#include<set>

using namespace std;
map<pair<int,int>,int> farm;
int casenumber=0;
int plant=0;
int w_protected=0;
set<int> x_set;
set<int> y_set;
set<int>::iterator x_begin;
set<int>::iterator y_begin;
int  x[1000];
int y[1000];
int x_length=0;
int y_length=0;
void getInput(istream& cin);//输入数据
int minArea(int,int,int,int);//计算最小面积的主方法
bool isLegal(int,int,int,int,int,int);//检测该点是否在当前范围内
int min(int,int,int,int);//计算分别删除四条边得到结果中的最小值
void reset();//进行下一个case之前清空数据


void getInput(istream& cin){
    cout<<"请输入有多少个case:"<<endl;
    cin>>casenumber;
    int i=0;
    while(i<casenumber){
        cout<<"多少颗西瓜藤,保护多少个?"<<endl;
        cin>>plant;
        cin>>w_protected;
        int j=0;
        while(j<plant){
            cout<<"西瓜种植情况 x,y,个数表示"<<endl;
            int x=0;
            int y=0;
            int w_number=0;
            cin>>x;
            cin>>y;
            cin>>w_number;
            x_set.insert(x);
            y_set.insert(y);
            farm.insert(map<pair<int,int>,int>::value_type(pair<int,int>(x,y),w_number));
            j++;
        }
        x_begin=x_set.begin();;
        for(x_begin;x_begin!=x_set.end();x_begin++){
            x[x_length]=*x_begin;
            x_length++;
        }
        y_begin=y_set.begin();
        for(y_begin;y_begin!=y_set.end();y_begin++){
            y[y_length]=*y_begin;
            y_length++;
        }
        x_length--;
        y_length--;
        int min=minArea(0,x_length,0,y_length);
        if(min==-1){
            cout<<"你确定你有那么多西瓜?"<<endl;
        }else{
            cout<<"你需要这么大面积的材料: "<<min<<endl;
        }
        i++;
        //完成一个case后需要重置变量
        reset();
    }    
}

void reset(){
    plant=0;
    w_protected=0;
    farm.clear();
    y_length=0;
    x_length=0;
}

int minArea(int x_min,int x_max,int y_min,int y_max){
    /*
    if(x_max==(x_min+1)&&y_max==(y_min+1)){
        return 1;
    }
    */
    map<pair<int,int>,int>::iterator iter;
    int total=0;
    for(iter=farm.begin();iter!=farm.end();iter++){
        pair<int,int> pair=iter->first;
        int wm_number=iter->second;
        int x_p=pair.first;
        int y_p=pair.second;
        if(isLegal(x[x_min],x[x_max],y[y_min],y[y_max],x_p,y_p)){
            total=total+wm_number;
        }

    }
    //如果合法则说明能够进行下一轮删减
    if(total>=w_protected){
        int area_1=0;
        int area_2=0;
        int area_3=0;
        int area_4=0;
        if(x_max!=x_min){
            area_1=minArea(x_min+1,x_max,y_min,y_max);
            area_2=minArea(x_min,x_max-1,y_min,y_max);
        }else{
            area_1=-1;
            area_2=-1;
        }
        if(y_max!=y_min){
            area_3=minArea(x_min,x_max,y_min+1,y_max);
            area_4=minArea(x_min,x_max,y_min,y_max-1);
        }else{
            area_3=-1;
            area_4=-1;
        }
        int min_area=min(area_1,area_2,area_3,area_4);
        if(min_area!=-1){
            return min_area;
        }
        else{
            int height=y[y_max]-y[y_min];
            int width=x[x_max]-x[x_min];
            if(height==0){
                height=1;
            }if(width==0){
                width=1;
            }
            return height*width;
        }
    }else{
        return -1;
    }
}

bool isLegal(int x_min,int x_max,int y_min,int y_max,int number_x,int number_y){
    if(x_min<=number_x&&number_x<=x_max&&y_min<=number_y&&number_y<=y_max){
        return true;
    }
    return false;
}
int min(int x1,int x2,int x3,int x4){
    int min=1000000;
    if(x1!=-1){
        min=x1;
    }
    if(x2<min&&x2!=-1){
        min=x2;
    }
    if(x3<min&&x3!=-1){
        min=x3;
    }
    if(x4<min&&x4!=-1){
        min=x4;
    }
    //如果都不能达到要求,则返回-1
    if(x1==-1&&x2==-1&&x3==-1&&x4==-1){
        return -1;
    }else{
        return min;
    }
}

int main(){
    cout<<"开始输入:"<<endl;
    getInput(cin);

}


时间复杂度也是4的次方。。。。呵呵哒。。。传上去果断TLE。

然后就没办法了,第二天看到别人的代码是从遍历了每个点,1000^4复杂度吧,但是比上面好,然后又改进了下,将每个点的y,x都排序出来,选出他们之间的排列组合,即x1,x2,y1,y2,然后每次都遍历了一下map,求这个范围的西瓜数是否满足条件。时间复杂度最大是1000^4*n,TLE,我已经放弃治疗了

但这次的代码远远比之前的简单,而且基本没错

#include<stdio.h>
#include<iostream>
#include<vector>
#include<map>
#include<set>

using namespace std;
map<pair<int,int>,int> farm;
int casenumber=0;
int plant=0;
int w_protected=0;
set<int> x_set;
set<int> y_set;
set<int>::iterator x_begin;
set<int>::iterator y_begin;
int  x[1000];
int y[1000];
int x_length=0;
int y_length=0;
void getInput(istream& cin);//输入数据
int minArea(int,int,int,int);//计算最小面积的主方法
bool isLegal(int,int,int,int,int,int);//检测该点是否在当前范围内
int min(int,int,int,int);//计算分别删除四条边得到结果中的最小值
void reset();//进行下一个case之前清空数据
int min_area=1000000;//最小面积
int count(int x1,int x2,int y1,int y2);//计算面积
void countArea();


void getInput(istream& cin){
	cout<<"请输入有多少个case:"<<endl;
	cin>>casenumber;
	int i=0;
	while(i<casenumber){
		//cout<<"多少颗西瓜藤,保护多少个?"<<endl;
		cin>>plant;
		cin>>w_protected;
		int j=0;
		while(j<plant){
			//cout<<"西瓜种植情况 x,y,个数表示"<<endl;
			int x=0;
			int y=0;
			int w_number=0;
			cin>>x;
			cin>>y;
			cin>>w_number;
			x_set.insert(x);
			y_set.insert(y);
			farm.insert(map<pair<int,int>,int>::value_type(pair<int,int>(x,y),w_number));
			j++;
		}
		x_begin=x_set.begin();;
		for(x_begin;x_begin!=x_set.end();x_begin++){
			x[x_length]=*x_begin;
			x_length++;
		}
		y_begin=y_set.begin();
		for(y_begin;y_begin!=y_set.end();y_begin++){
			y[y_length]=*y_begin;
			y_length++;
		}
		x_length--;
		y_length--;
//		int min=minArea(0,x_length,0,y_length);
		countArea();
		int min=0;
		if(min_area==1000000){
			min=-1;
		}else{
			min=min_area;
		}
		if(min==-1){
			cout<<"你确定你有那么多西瓜?"<<endl;
		}else{
			cout<<"你需要这么大面积的材料: "<<min<<endl;
		}
		i++;
		//完成一个case后需要重置变量
		reset();
	}	
}

void reset(){
	plant=0;
	w_protected=0;
	farm.clear();
	y_length=0;
	x_length=0;
	min_area=1000000;
}

void countArea(){
	map<pair<int,int>,int>::iterator iter;
	for(int i=0;i<=x_length;i++){
		for(int j=i;j<=x_length;j++){
			for(int m=0;m<=y_length;m++){
				for(int n=m;n<=y_length;n++){
					int total=0;
					int x1=x[i];
					int x2=x[j];
					int y1=y[m];
					int y2=y[n];
					for(iter=farm.begin();iter!=farm.end();iter++){
						pair<int,int> pair=iter->first;
						int x=pair.first;
						int y=pair.second;
						if(isLegal(x1,x2,y1,y2,x,y)){
							total=total+iter->second;
						}
					}
					if(total>=w_protected){
						int area=count(x1,x2,y1,y2);
						if(min_area>area){
							min_area=area;
						}
					}
				}
			}
		}
	}
	
}
int count(int x1,int x2,int y1,int y2){
	int height=x2-x1;
	int width=y2-y1;
	if(height==0){
		height=1;
	}
	if(width==0){
		width=1;
	}
	return height*width;
}

int minArea(int x_min,int x_max,int y_min,int y_max){
	/*
	if(x_max==(x_min+1)&&y_max==(y_min+1)){
		return 1;
	}
	*/
	map<pair<int,int>,int>::iterator iter;
	int total=0;
	for(iter=farm.begin();iter!=farm.end();iter++){
		pair<int,int> pair=iter->first;
		int wm_number=iter->second;
		int x_p=pair.first;
		int y_p=pair.second;
		if(isLegal(x[x_min],x[x_max],y[y_min],y[y_max],x_p,y_p)){
			total=total+wm_number;
		}

	}
	//如果合法则说明能够进行下一轮删减
	if(total>=w_protected){
		int area_1=0;
		int area_2=0;
		int area_3=0;
		int area_4=0;
		if(x_max!=x_min){
			area_1=minArea(x_min+1,x_max,y_min,y_max);
			area_2=minArea(x_min,x_max-1,y_min,y_max);
		}else{
			area_1=-1;
			area_2=-1;
		}
		if(y_max!=y_min){
			area_3=minArea(x_min,x_max,y_min+1,y_max);
			area_4=minArea(x_min,x_max,y_min,y_max-1);
		}else{
			area_3=-1;
			area_4=-1;
		}
		int min_area=min(area_1,area_2,area_3,area_4);
		if(min_area!=-1){
			return min_area;
		}
		else{
			int height=y[y_max]-y[y_min];
			int width=x[x_max]-x[x_min];
			if(height==0){
				height=1;
			}if(width==0){
				width=1;
			}
			return height*width;
		}
	}else{
		return -1;
	}
}

bool isLegal(int x_min,int x_max,int y_min,int y_max,int number_x,int number_y){
	if(x_min<=number_x&&number_x<=x_max&&y_min<=number_y&&number_y<=y_max){
		return true;
	}
	return false;
}
int min(int x1,int x2,int x3,int x4){
	int min=1000000;
	if(x1!=-1){
		min=x1;
	}
	if(x2<min&&x2!=-1){
		min=x2;
	}
	if(x3<min&&x3!=-1){
		min=x3;
	}
	if(x4<min&&x4!=-1){
		min=x4;
	}
	//如果都不能达到要求,则返回-1
	if(x1==-1&&x2==-1&&x3==-1&&x4==-1){
		return -1;
	}else{
		return min;
	}
}

int main(){
	cout<<"开始输入:"<<endl;
	getInput(cin);

}

到现在为止在网上看到有大神的代码,上传跑出来也是正确的,而且量还很少,但是我目前还不是很理解这代码。。。。。贴这些破代码上来是为了表示下我真的写了这道题。。。关于C++有些容器啊什么的使用技巧感觉完全不懂啊,写完第三版发现完全没有必要用map啊。。。不过比数组节省空间

好了就这样吧。。。等我看看大神的代码。。。

http://blog.csdn.net/anqier0468/article/details/9621145  贴个链接。。。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值