Posts Tagged ‘POJ’

POJ1062【单源最短路】

原题描述:
[url=http://acm.pku.edu.cn/JudgeOnline/problem?id=1062]见原题[/url]

题目大意:
探险家想获得某样物品,可以直接买,花费P;也可以获得另外一种物品(递归调用。。。),再加上V金币来换取。
另外,与探险家交换的人都有个级别。他只能在一个长度为M + 1的下界不定的范围的级别的人内交换。举个例子,M为1,他要是与级别为3的人交换了,就不能与级别为1的人交换了,反之亦然。
求出,探险家想获取物品0所需花费的最少金币数。

解题思路:
我的想法是,在可以互换的物品间建立一条边,度为其需要的额外金币V。这样便建起了一个图。以0为起始点,用dijkstra算法求出它到每个点的最短 路。这个最短路便是,用该物品经过多次换算,得到物品0所需的最少的金币数。再加上买这个物品的费用,便是获得物品0所需的最小金币数了。
另外,这里还有一个问题,就是每个人都级别的问题了。我是通过两个数组,分别保存换取到物品i时的最大的级别和最小的级别。于是,在dijkstra比较时,加入级别的判定。这样,便能轻松的解决这个问题。

代码:

#include <stdio.h>
#define MAXN 101
#define inf 1000000000
typedef int elem_t;
int Lev[101];
int Lev_lowest[101],Lev_max[101];
int M,N;

void dijkstra(int n,elem_t mat[][MAXN],int s,elem_t* min,int* pre){
int v[MAXN],i,j,k;
for (i=0;i<n;i++)
min=inf,v=0,pre=-1;
for (min[s]=0,j=0;j<n;j++){
for (k=-1,i=0;i<n;i++){
if (!v&&(k==-1||min<min[k])){
k=i;
}
}
for (v[k]=1,i=0;i<n;i++){
if (!v&&min[k]+mat[k]<min && (Lev <= M + Lev_lowest[k]) && Lev >= Lev_max[k] - M){
min=min[k]+mat[pre=k];
Lev_lowest = Lev_lowest < Lev_lowest[k]?Lev_lowestev_lowest[k];
Lev_max = Lev_max > Lev_max[k]?Lev_maxev_max[k];
}
}
}
}

int map_wedding[101][101];

int _abs(int x){
if(x >= 0){
return x;
}
return -x;
}

int main(){
while(scanf("%d %d",&M,&N) != EOF){
int i,k,z;
for(i = 0; i <= N; ++i){
for(k = 0; k <= N; ++k){
map_wedding[k] = inf;
}
}
int P[101],X;
int max_l = -1;
for(i = 0; i < N; ++i){
scanf("%d %d %d",&,&Lev,&X);
Lev_lowest = Lev_max = Lev;
for(k = 0; k < X; ++k){
int T,V;
scanf("%d %d",&T,&V);
T--;
map_wedding[T] = V;
}
}
for(i = 0; i < N; ++i){
for(k = i; k < N; ++k){
if(map_wedding[k] != inf){
if(_abs(Lev - Lev[k]) > M){
map_wedding[k] = map_wedding[k] = inf;
}
}
}

}
//for(i = 0; i < N; ++i){
//        for(k = 0; k < N; ++k){
//                printf("%d\t",map_wedding[k]);
//        }
//        printf("\n");
//}
int min[101],pre[101];
dijkstra(N,map_wedding,0,min,pre);
int min_cost = P[0];
for(i = 1; i < N; ++i){
//        printf("%d %d\t",min,pre);
min_cost = min_cost > min + P?min + P:min_cost;
}
//printf("\n");
printf("%d\n",min_cost);
}
return 0;
}

测试数据
1 4
10000 3 2
2 8000
3 5000
1000 2 1
4 200
3000 2 1
4 200
50 2 0
1 4
10000 2 1
2 4000
6000 3 1
3 2000
3000 2 1
4 1000
1000 1 0

结果
5250
9000

以上代码未经过优化。

Incoming search terms for the article:

POJ 1837 Balance解题报告(动态规划)

见原文
NENU Goodness
题目来源:POJ 1837 Balance
http://acm.pku.edu.cn/JudgeOnline/problem?id=1837

解法类型:动态规划

题目大意:
输入一个天平若干(<=20)挂钩的位置,将若干(<=20)砝码挂到天平上,问有多少种使天平挂平衡的方法。

解题思路:
用一个二维数组t[x][y+4000]记录挂x个砝码时到y这个值的方法数,将砝码一一挂上,最后记录所有砝码都挂上时的t[x][4000]的值,详见代码。

提交情况:
Wrong Answer若干:开始的时候没想到用二维数组,试图通过两个一维数组不断互相更新,由于操作繁琐,过需花费时间,导致不能遍历每个位置,为了省时,又引 入 记录 值不为0 的t数组相应位置 的数组。导致操作更加繁琐,代码过长,思路虽没什么问题,但零星的错误实在难以找到,最终只好更换思路。

注意:
动态规划类题目状态标记方法 以及标记数组各元素之间的关系 比较多样灵活, 难以想到,这是重点也是难点。

代码:

源程序:
#include <iostream>

using namespace std;

int w[21],h[21],t[21][8000];

/*w数组用来记录砝码的重量,
h数组用来记录天平挂钩的位置,
t的第一位维用来记录已挂上的挂钩的数目
第二维用来记录通过挂砝码
可以到达相应值的方法数*/

int main()
{
int c,g,i,j,k;
while(cin>>c>>g)
{
for(i=1;i<=c;i++) cin>>h[i];        //输入挂钩位置和砝码的重量
for(i=1;i<=g;i++) cin>>w[i];
memset(t,0,sizeof(t));                        //初始化可达数数组
for(i=1;i<=c;i++) t[1][w[1]*h[i]+4000]++;

/*挂第一个砝码时对可达数 数组
进行相应的修改 其中+4000
(可知若想使得天平平衡一面最多挂到的值为4000)
是为了将所有值都转到正数组中 方便处理
开始的时候没想到 用了两个数组
这也给编码和调试带来了极大的不便*/

for(i=2;i<=g;i++)                //将剩余的砝码一一挂上
for(j=1;j<=c;j++)        //循环所有挂钩的位置
for(k=1;k<=8000;k++)
if(t[i-1][k]) t[i][k+w[i]*h[j]]+=t[i-1][k];

/*循环所有值  找到 挂  上一个  砝码后可以达到的值,
然后挂当前砝码,得到 挂当前砝码后
计算得到的对应值 的可达数*/

cout<<t[g][4000]<<endl;

//输出所有砝码都挂上 平衡位置所对应的可达数即为 平衡的方法数
}
return 0;
}

另可见,我(Charlie)在上边有些论述:

http://www.5ushare.com/bbs/viewthread.php?tid=56&extra=page%3D1

http://www.5ushare.com/bbs/viewthread.php?tid=29&extra=page%3D1

Incoming search terms for the article:

POJ 2917【数学题】

原文见

代码:

POJ 2917题解题报告 数学题
原题描述:
Diophantus of Alexandria
Time Limit: 1000MS                Memory Limit: 65536K
Total Submissions: 2298                Accepted: 669
Description
Diophantus of Alexandria was an Egypt mathematician living in Alexandria. He was one of the first mathematicians to study equations where variables were restricted to integral values. In honor of him, these equations are commonly called Diophantine equations. One of the most famous Diophantine equation is xn + yn = zn. Fermat suggested that for n > 2, there are no solutions with positive integral values for x, y and z. A proof of this theorem (called Fermat’s last theorem) was found only recently by Andrew Wiles.
Consider the following Diophantine equation:
(1)
Diophantus is interested in the following question: for a given n, how many distinct solutions (i. e., solutions satisfying x ≤ y) does equation (1) have? For example, for n = 4, there are exactly three distinct solutions:

Clearly, enumerating these solutions can become tedious for bigger values of n. Can you help Diophantus compute the number of distinct solutions for big values of n quickly?
Input
The first line contains the number of scenarios. Each scenario consists of one line containing a single number n (1 ≤ n ≤ 109).
Output
The output for every scenario begins with a line containing “Scenario #i:”, where i is the number of the scenario starting at 1. Next, print a single line with the number of distinct solutions of equation (1) for the given value of n. Terminate each scenario with a blank line.
Sample Input
2
4
1260
Sample Output
Scenario #1:
3

Scenario #2:
113
Source
TUD Programming Contest 2006, Darmstadt, Germany

题目大意:
等式 1/x + 1/y = 1/n  x,y,n都为正整数且x<y;
给定n值,输出有多少对这样的x,y;
题意理解
此题主要是时间上要求很严格。不能直接用遍历(1/y = (x – n)/(x*n) 遍历判断x*n能否被(x – n)整除),这样时间不够用。

所以,采用数学公式直接求解,可将此等式转换为 (x – z)(y – z) = z^2  这样就是求Z^2的约数中小于等于z的个数(即问题的答案);

到这里,求一个数的约数的个数有一个算法。就是先求一个素数表,将这个数逐一试除。

由于,Z取到10^9,那么直接求(10^9)^2将会越界(int)。并且时间上也很难过去。

Z^2所得的完全分解表每一项的个数应该是Z所得的完全分解表的对应项的2倍;
在根据 Yn = X 1^a1 *X2^a2 * …*Xn^an; Yn的约数个数等于(a1 + 1)(a2 + 1)…(an + 1);(对每一项Xn有an种选法)(1)

这样就先求的Z的分解式,将每一项×2就是Z^2对应的个数。在根据公式(1)就求得Z^2的约数res,而小于Z的约数 = (res + 1)/2
题目的主要考察点:
1.        数学分析能力。(公式的推导)
2.        基础小程序的熟练程度。(求小于n 的素数。分解一个数)
数据范围:
题目数据范围: 1=<N <= 10^9
题目陷阱:
需要注意的地方:
素数表只需求到Sqrt(N)即可;(可节省很多时间)
解题思路:
根据以上分析:
1.先打出32000以内的素数表。(Sqrt(10^2) < 32000)
2.对N进行分解,将每一项的个数存到数组num[]中;
3.利用res *= (2*num + 1) 求出约数个数,最后输出(res + 1)/2;
AC代码:[code]
#include<stdio.h>
#include<math.h>
bool flag[33200]= {0};
int pri[5000];
int prime(int n)
{
n++;
n /= 2;
int i,k,kk;
pri[0] = 2;
pri[1] = 3;
int num = 2;
for(i = 3; i<n; i+=2)
{
for(k = 0; k< 2; ++k)
{
int temp = (i+k)*2 - 1;
if(flag[temp] == 0)
{
pri[num++] = temp;
for(kk = temp; kk< 2*n; kk+=temp)
{
flag[kk] = 1;
}
}
}
}
return num; //素数个数
}

int reduce(int pN,int n)
{

int i,index = 0;
int num[200]= {0};  //记录每个Xn的an个数
for(i = 0; i < pN; ++i)
{
if(n == 1) break;
if(pri*pri>n) //如果n是一个大于sqrt(n)的素数
{
num[index++] = 1;
break;
}
int k = 0;
while(n % pri == 0)
{
k++;
n /= pri;
}
if(k != 0)
num[index++] = k;
}
int res = 1;
for(i=0; i<index; i++)
{
res *= (2*num+1);
}
return res;
}

int main()
{
int casN,cas,N;
scanf("%d",&casN);
int pN = prime(32000);
for(cas = 1; cas <= casN; cas++)
{
scanf("%d",&N);
int res = reduce(pN,N);
printf("Scenario #%d:\n%d\n\n",cas,(res+1)/2);
}
return 0;
}

复杂度:

O(n^2)

Incoming search terms for the article:

POJ1836【动态规划】

原文见
原题描述:
Alignment
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 1188 Accepted: 239
Description
In the army, a platoon is composed by n soldiers. During the morning inspection, the soldiers are aligned in a straight line in front of the captain. The captain is not satisfied with the way his soldiers are aligned; it is true that the soldiers are aligned in order by their code number: 1 , 2 , 3 , . . . , n , but they are not aligned by their height. The captain asks some soldiers to get out of the line, as the soldiers that remain in the line, without changing their places, but getting closer, to form a new line, where each soldier can see by looking lengthwise the line at least one of the line’s extremity (left or right). A soldier see an extremity if there isn’t any soldiers with a higher or equal height than his height between him and that extremity.

Write a program that, knowing the height of each soldier, determines the minimum number of soldiers which have to get out of line.
Input
On the first line of the input is written the number of the soldiers n. On the second line is written a series of n floating numbers with at most 5 digits precision and separated by a space character. The k-th number from this line represents the height of the soldier who has the code k (1 <= k <= n).

There are some restrictions:
• 2 <= n <= 1000
• the height are floating numbers from the interval [0.5, 2.5]
Output
The only line of output will contain the number of the soldiers who have to get out of the line.
Sample Input
8
1.86 1.86 1.30621 2 1.4 1 1.97 2.2
Sample Output
4

题目大意:
给出系列数字,要求求出一个最长串,使得任意一个数字都不在两个大于等于它的数字中间
题意理解
这题主要是要求一个不降序列和一个不升序列的序列,由此,可以看到跟2533(最长不降序列)很相像。实际上,也只要考虑几个小问题就行了。
解题思路:
在2533(最长不降序列)中,我们只要用一个1000的数组就行了。其状态转移方程为
F(K) = MAX(F(A1),F(A2)..F(Ai)) + 1,其中0=<A1=<A2..<Ai=<N,Seq[k] >= F(Ai)。即第K个数字及其之前数字的最长不降序列为其之前的某个小于它的数字的最长不降序列加一。
对于这题,则可用一个二维数组。其第一维与2533相同,储存其最长不降序列;第二维则储存最长不升序列。具体为:
F(K,0)=MAX(F(A1,0)…F(Ai,0)) + 1; F(K,1) = MAX(F(B1,0)..F(Bi,0),F(A1,1)..F(Bi,1)) + 1.
另外,还有一点需要注意,就是 5 5 5中间的5 的处理。。。
具体代码如下:
AC代码:

复制内容到剪贴板

代码:

#include <iostream>
using namespace std;

int main(){
int n;
double Num[1000];
while(cin >> n){
for(int i = 0; i < n; ++i){
cin >> Num[i];
}
int Res[2001][3];
for(int i = 0; i < n; ++i){
Res[i][0] = 1;
Res[i][1] = 1;
Res[i][2] = 0;
}
for(int i = 0; i < n; ++i){
for(int k = 0; k < i; ++k){
if((Num[i] < Num[k]) || (!Res[k][2] && Num[i] == Num[k])){
if(Res[i][1] < Res[k][0] + 1){
Res[i][1] = Res[k][0] + 1;
Res[i][2] = 1;
}
if(Res[i][1] < Res[k][1] + 1){
Res[i][1] = Res[k][1] + 1;
Res[i][2] = 1;
}
}else if(Num[i] > Num[k]){
if(Res[i][0] < Res[k][0] + 1){
Res[i][0] = Res[k][0] + 1;
}
}
}
}
int max = 0;
for(int i = 0; i < n; ++i){
if(Res[i][0] > max){
max = Res[i][0];
}
if(Res[i][1] > max){
max = Res[i][1];
}
}
cout << n – max << endl;
}
}

Incoming search terms for the article:

POJ3681[Finding the Rectangle]【枚举+限界】

原文见
【原题链接】
http://acm.pku.edu.cn/JudgeOnline/problem?id=3681
【题意描述】
给出N、M和N个点的坐标,要求找出面积最小的一个矩形,使其中至少包含M个点(恰处在边上的点不算包含在内)。
【数据范围】
1 ≤ M ≤ N ≤ 200
1 ≤ Xi, Yi ≤ 10000 ,(Xi,Yi)为第i个点坐标
【解题思路】
该题直观上看似乎有特殊办法对解进行构造,但仔细一想,也没有什么好的策略(至少我没有想出来),包括贪心、动态规划都不大适用。观察数据量,点的数量最 多为200,则最多不同的X坐标和Y坐标分别为200,若将坐标离散化,则最多有200×200个坐标,则可能的矩形数为 O(N^4)≤1600000000。这样看来数据量也不算太大,如果进行适当的剪枝和限界则可行解的数量更会大大减少。于是决定使用最普通的枚举方法, 加上适当的剪枝和限界。
这道题首先要做对数据做一些处理,直接在原坐标系上进行枚举肯定是不合适的,因为一共有10000^2个坐标点,而实际给出的点只有200个。因此需要将 坐标离散化,即只留下那些上面有点出现的坐标线。这里先要考虑一个问题,题目中说,矩形边上的点不算包含在矩形内,这样,矩形的顶点不就有可能出现在没有 实际点的坐标线上了吗,但是仔细想想,并不用这样考虑,因为矩形有边上都不包含点,因此每个矩形的最外面一层都是无用的(因为不包含点),故在选择矩形的 时候假设矩形的四个顶点都向内收缩一个单位,而在计算其面积的时候将其连长各加上二之后在相乘,也就是说,我们在枚举矩形的时候,枚举的是其边上的点计算 在内的矩形顶点,而在计算矩形面积的时候再将其还原。有了这个等价的条件,我们可以有允足的理由将坐标系离散化了,因为要寻找的是最小的矩形,为使每个矩 形包含尽可能多的点,我们枚举的矩形要在其边上尽可能地包含点(因为若边上不包含点,则包含同样这些点的矩形可以再缩小,注意,我们这里说的矩形是指顶点 经过收缩的矩形,以下均这么说)。因此,矩形的顶点只可能出现在其X、Y坐标线上有点的坐标上,而其X、Y坐标线上没有点的坐标不予考虑,所以我们选择将 坐标系离散化。离散化结果是,将坐标系中所有可能的X轴坐标仅为在所给点中出现的X坐标,Y轴坐标亦然。具体用下图来说明,

若原有坐标系中有三个红点,则依据这三个点进行离散化后的坐标系中的坐标线只剩下图中加粗的线。离散化的具体做法是,先将点依据X坐标排序,然后将所有不 同的X轴坐标做为一条X轴坐标线保存,并记录其在原坐标系上的值,同时,依据该坐标线在新坐标系中的排序给点分配新的X坐标。对Y轴的离散化做法与之类 似。
第二个处理是,在新坐标系上记录各坐标上的点的数目。将坐标系离散化后,将用一个二维数组MAP表示新的坐标系,其大小为200×200。因为在对坐标进 行离散化的时候已经将点的坐标转换化新坐标系中的坐标,因此只要直接对MAP[point.x][point.y]进行累加就可以了。
第三个处理是,在新坐标系中计算从坐标系的(0,0)-(x,y)的矩形内的点的数目。因为之后的枚举中需要用到计算(x0,y0)-(x1,y1)的矩 形内的点数,因些在先打表计算出所有(0,0)-(x,y)矩形的点数可以加快之后的计算。具体的算法是逐行逐列的累加,时间复杂度为O(N^2)。使用 一个数组Cache存储已计算过的行中每一列的点数,由于计算时先沿列走再沿行走,故在同一行中,将要计算的坐标(x,y)的点数为刚计算完的(x,y- 1)的点数,加上前x-1行y列累加的点数,再加上坐标点(x,y)上的点数。即运用下式
num(i,j)=num(i,j-1)+cache(j)+map(i,j)
之后再累加Cache,即
cache(j)+=map(i,j)
则把每个坐标点遍历一遍即可算出每个以(0,0)-(x,y)的矩形内的点的数目。
完成这些处理工作之后可以开始进行枚举了。枚举的方法很简单,四重循环,枚举出两个顶点,先计算其面积是否比当前的最小面积小,若是,则计算矩形内的点数 是否足够M,若还是,则修改最小面积。只是如果直接枚举的话速度还是太慢,好在这里还有许多可以优化的地方。枚举中的四重循环有四个变量i,j,k,h, 我们对每个变量都可以进行限界,以排除明显不可能得到解的情况。首先说明这四个量的含义,他们构成对角两个顶点(i,j)-(k,h)组成的矩形,此处 k≥i,h≥j,另有两个量cx,cy表示x,y坐标的最大范围。对这四个量的具体限界如下:
对i来说,若从该行的第一列起,到图形的右下角,即(i,1)-(cx,cy)构成的矩形中的点数都不足M的话,则继续枚举任何一个点都不再可能有解,即故跳出循环;
对j来说,若在某一个点(i,j)-(cx,cy)构成的矩形中的点数不足M的话,则对任意i’ ≥i,j’>j,(i’,j’)-(k,h)构成的矩形中的点数都不能达到M,因为k≤cx,h≤cy,该矩形被包括在矩形 (i,j)-(cx,cy)内,因此进入下一行循环的时候,循环变量j也不必达到或超过当前的j。故此时的j成为循环变量j的新界,我们用lj保存j的上 界,即此时,lj=j-1。
对k来说,首先计算矩形(i,j)-(k,cy)内的点数是否足够M,若不足则明显无法在该行找到合适的h,k进入下一行。另外,当i进入下一行的同一列 后,k的起始循环量也不会小于上一行中M点足够的第一个k。故用一个数组lk存储k的下界,lk[j’]为j=j’时k的起始循环量。
对h来说,当某个矩形(i,j)-(k,h)的原面积大于当前最小面积的时候,则对于任何h’>h,k’>k,(i,j)-(k’,h’)矩 形的面积也必定大于最小面积,故不必枚举,此时可对h限上界,别外,当矩形(i,j)-(k,h)内包含的点足够M之后,也不必枚举矩形 (i,j)-(k’,h’)的情况,此时也应限界,对h 的限界方法与对j的限界方法类似。
利用上述四个限界方法,可使枚举的情况大大减少。

【具体代码】

复制内容到剪贴板

代码:

#include <iostream>
using namespace std;

struct point_elem{int x,y;} point[200]; //用于存放点坐标
int coordx[201];    //用于存放离散化后的X坐标
int coordy[201];    //用于存放离散化后的Y坐标
int cx;             //离散X坐标的数目
int cy;             //离散Y坐标的数目
int map[201][201];  //用于存放离散化后的坐标上的点的数目
int num[201][201];  //用于统计从左上角到该点坐标的点的数目
int n,m;            //存储题目中读入的N个点,及要包含在矩形内的M个点

//比较函数,比较X坐标的大小,用于qsort排序
int cmp_by_x(const void *cp1,const void *cp2)
{
return ((point_elem*)cp1)->x-((point_elem*)cp2)->x;
}

//比较函数,比较Y坐标的大小,用于qsort排序
int cmp_by_y(const void *cp1,const void *cp2)
{
return ((point_elem*)cp1)->y-((point_elem*)cp2)->y;
}

//创建离散化的X坐标
int create_coordx()
{
coordx[1]=point[0].x;
int k=1;
for (int i=0;i<n;i++)
{
//为每个不同的X坐标创建一条X坐标
if (point.x!=coordx[k])
{
coordx[++k]=point.x;
}
point.x=k;
}
return k;
}

//创建离散化的Y坐标
int create_coordy()
{
coordy[1]=point[0].y;
int k=1;
for (int i=0;i<n;i++)
{
//为每个不同的Y坐标创建一条Y坐标
if (point.y!=coordy[k])
{
coordy[++k]=point.y;
}
point.y=k;
}
return k;
}

//将原有点标在离散化后的坐标系上
void create_map()
{
memset(map,0,sizeof(map));
for (int i=0;i<n;i++)
{
map[point.x][point.y]++;
}
}

//统计坐标系上,每个坐标的左上方的所有输入点的数目
void create_num()
{
memset(num,0,sizeof(num));
int cache[201]={0};
for (int i=1;i<=cx;i++)
{
for (int j=1;j<=cy;j++)
{
num[j]=num[j-1]+cache[j]+map[j];
cache[j]+=map[j];
}
}
}

//计算(x0,y0),(x1,y1)之间所有输入点的数目
inline int calpoint(int x0,int y0,int x1,int y1)
{
return num[x1][y1]-num[x1][y0-1]-num[x0-1][y1]+num[x0-1][y0-1];
}

//枚举矩形的左上角和右下角,计算其中的点数,找出符合条件的最小矩形
int find()
{
int min=2000000000;
int lj=cy;  //对j进行限界
int lk[201];//对k进行限界
for (int i=1;i<=cy;i++) lk=1;
for (int i=1;i<=cx;i++)
{
if (calpoint(i,1,cx,cy)<m) break;
for (int j=1;j<=lj;j++)
{
if (calpoint(i,j,cx,cy)<m)
{
lj=j-1;
break;
}
int lh=cy;//对h进行限界
if (lk[j]<i) lk[j]=i;
for (int k=lk[j];k<=cx;k++)
{
if (calpoint(i,j,k,lh)<m)
{
if (lh==cy) lk[j]++;
continue;
}
for (int h=j;h<=lh;h++)
{
if ((coordx[k]-coordx+2)*(coordy[h]-coordy[j]+2)<min)
{
if (calpoint(i,j,k,h)>=m)
{
min=(coordx[k]-coordx+2)*(coordy[h]-coordy[j]+2);
lh=h-1;
}
}
else
{
lh=h-1;
}
}
}
}
}
return min;
}

int main()
{
int ca;
cin>>ca;
for (int no=0;no<ca;no++)
{
//输入数据
cin>>n>>m;
for (int i=0;i<n;i++)
{
scanf(“%d %d”,&point.x,&point.y);
}
//依据点的X坐标进行排序后创建离散的X坐标
qsort(point,n,sizeof(point_elem),cmp_by_x);
cx=create_coordx();
//依据点的Y坐标进行排序后创建离散的Y坐标
qsort(point,n,sizeof(point_elem),cmp_by_y);
cy=create_coordy();
//在离散后的坐标上标点
create_map();
//计算从左上角到每个坐标的点数并打表
create_num();
//查找最小的矩形
cout<<find()<<endl;
}
return 0;
}

【AC感想】
这道题是POJ七月份月赛中最简单的一道题,到目前为止通过的人数也最多。那天比赛的时候也尝试了这道题的枚举方法,只是没加上那么多的限界,时间上无法通过,于是认为此题有更神奇的方法,便没再考虑。后来再稍加限界后发现效果明显才继续研究。
在做过了这道题之后,我体会到一点,有时候巧妙地使用最暴力的方法也能做出一些题目。这道题枚举的方法也可以看作是搜索,深度为4,每层最多扩展200个结点,其限界的方法与剪枝的思想是相同,因此这道题更加让我体会了剪枝的重要性,巧妙的剪枝可以大大减少枚举的情况。
另外此题中对坐标的离散化处理也是一种重要的手段,而打表事先计算(0,0)-(x,y)的点数也是加快运算的好办法,值得回味。

POJ2418【二叉搜索树】

POJ2418【二叉搜索树】

题目描述:
Hardwood Species

Time Limit: 10000MS Memory Limit: 65536K
Total Submissions: 3576 Accepted: 1421

Description
Hardwoodsare the botanical group of trees that have broad leaves, produce afruit or nut, and generally go dormant in the winter.
America’s temperate climates produce forests with hundreds ofhardwood species — trees that share certain biologicalcharacteristics. Although oak, maple and cherry all are types ofhardwood trees, for example, they are different species. Together, allthe hardwood species represent 40 percent of the trees in the UnitedStates.

On the other hand, softwoods, or conifers, from the Latin wordmeaning “cone-bearing,” have needles. Widely available US softwoodsinclude cedar, fir, hemlock, pine, redwood, spruce and cypress. In ahome, the softwoods are used primarily as structural lumber such as2x4s and 2x6s, with some limited decorative applications.

Using satellite imaging technology, the Department of NaturalResources has compiled an inventory of every tree standing on aparticular day. You are to compute the total fraction of the treepopulation represented by each species.
Input
Inputto your program consists of a list of the species of every treeobserved by the satellite; one tree per line. No species name exceeds30 characters. There are no more than 10,000 species and no more than1,000,000 trees.
Output
Printthe name of each species represented in the population, in alphabeticalorder, followed by the percentage of the population it represents, to 4decimal places.
Sample Input
Red Alder
Ash
Aspen
Basswood
Ash
Beech
Yellow Birch
Ash
Cherry
Cottonwood
Ash
Cypress
Red Elm
Gum
Hackberry
White Oak
Hickory
Pecan
Hard Maple
White Oak
Soft Maple
Red Oak
Red Oak
White Oak
Poplan
Sassafras
Sycamore
Black Walnut
Willow
Sample Output
Ash 13.7931
Aspen 3.4483
Basswood 3.4483
Beech 3.4483
Black Walnut 3.4483
Cherry 3.4483
Cottonwood 3.4483
Cypress 3.4483
Gum 3.4483
Hackberry 3.4483
Hard Maple 3.4483
Hickory 3.4483
Pecan 3.4483
Poplan 3.4483
Red Alder 3.4483
Red Elm 3.4483
Red Oak 6.8966
Sassafras 3.4483
Soft Maple 3.4483
Sycamore 3.4483
White Oak 10.3448
Willow 3.4483
Yellow Birch 3.4483
Hint
This problem has huge input, use scanf instead of cin to avoid time limit exceeded.
题目大意:
有一大堆树,卫星上看到他们的种类,输入数据给出的;让你统计每一个种类的出现次数,按照字典序输出种类名称和频率;
数据范围:
树种类名字小于30字符;种类个数小于1000;树的个数小于1000000;
边界数据;
无;
可能陷进
知道不能直接统计就好;
解题思路:
1000000估计硬统计肯定难过;所以就找一种方法吧,让查找尽量的快;本题用二叉搜索树;建树时通过比较字符串的值;最后再输出;
AC代码:
#include “stdio.h”
#include “stdlib.h”
#include “string.h”
int totalnum = 1;
typedef struct _bst
{
double count;
char name[31];
struct _bst *left;
struct _bst *right;
}BST;
BST *head = NULL;
void insert(char *tmp)
{
int t;
BST *p1, *p2;
BST *_node;
p1 = head;
while(p1 != NULL)
{
p2 = p1;
t = strcmp(tmp, p1->name);
if (t == 0)
{
p1->count++;
return;
}
else if (t > 0)
{
p1 = p1->right;
continue;
}
else
{
p1 = p1->left;
continue;
}
}
_node = (BST *)malloc(sizeof(BST));
_node->count = 1;
_node->left = NULL;
_node->right = NULL;
strcpy(_node->name, tmp);
if (t > 0)
{
p2->right = _node;
}
else
{
p2->left = _node;
}
}
void output(BST *phead)
{
if (phead != NULL)
{
output(phead->left);
printf(“%s %.4f\n”, phead->name, (phead->count*100)/totalnum);
output(phead->right);
}
}
int main()
{

//freopen(“e:\\in.txt”, “r”, stdin);
char temp[31];
totalnum = 1;
head = (BST *)malloc(sizeof(BST));
head->count = 1;
head->left = NULL;
head->right = NULL;
gets(head->name);
while(gets(temp))
{
if (temp[0] == ‘\0′) break;
totalnum++;
insert(temp);
}
output(head);
return 0;
}
【可优化】

POJ2524【并查集】

POJ2524【并查集】

题目描述:

Ubiquitous Religions

Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 5586 Accepted: 2346

Description
Thereare so many different religions in the world today that it is difficultto keep track of them all. You are interested in finding out how manydifferent religions students in your university believe in.

You know that there are n students in your university (0 < n<= 50000). It is infeasible for you to ask every student theirreligious beliefs. Furthermore, many students are not comfortableexpressing their beliefs. One way to avoid these problems is to ask m(0 <= m <= n(n-1)/2) pairs of students and ask them whether theybelieve in the same religion (e.g. they may know if they both attendthe same church). From this data, you may not know what each personbelieves in, but you can get an idea of the upper bound of how manydifferent religions can be possibly represented on campus. You mayassume that each student subscribes to at most one religion.
Input
Theinput consists of a number of cases. Each case starts with a linespecifying the integers n and m. The next m lines each consists of twointegers i and j, specifying that students i and j believe in the samereligion. The students are numbered 1 to n. The end of input isspecified by a line in which n = m = 0.
Output
Foreach test case, print on a single line the case number (starting with1) followed by the maximum number of different religions that thestudents in the university believe in.
Sample Input
10 9
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
10 4
2 3
4 5
4 8
5 8
0 0
Sample Output
Case 1: 1
Case 2: 7
Hint
Huge input, scanf is recommended.
题目大意:
给出几个人两两之间的信仰相同;要求给出最多有多少个不同的信仰集合;
数据范围:
学生数0—50000;
边界数据:
几乎没有;
可能陷进:
几乎没有;
AC代码:
#include “stdio.h”
#include “iostream”
using namespace std;
int root[100001];
int heav[100001];
void makeset(int i)
{
root = i;
heav = 1;
}
int findset(int i)
{
if (root != i)
root = findset(root);
return root;
}
void unio(int i, int j)
{
if (heav > heav[j])
{
root[j] = i;
heav = heav + heav[j];
}
else if (heav < heav[j])
{
root = j;
heav[j] = heav + heav[j];
}
else
{
root[j] = i;
heav = heav + heav[j];
}
}
int main()
{
//freopen(“e:\\in.txt”, “r”, stdin);
int count, n;
int p, q;
int t, k;
int testcase = 0;
while(scanf(“%d%d”, &count, &n) != EOF)
{
if (count == 0 && n == 0) break;
testcase++;
for (int i = 1; i <= count; ++i)
makeset(i);
while(n–)
{
scanf(“%d%d”, &p,&q);
t = findset(p);
k = findset(q);
if (t != k)
{
count–;
unio(t, k);
}

}
printf(“Case %d: %d\n”,testcase, count);
}
return 0;
}

POJ2051【最小优先级队列】【堆实现】

题目描述:
Argus
Time Limit: 1000MS                 Memory Limit: 30000K
Total Submissions: 4468                 Accepted: 1916

Description
A data stream is a real-time, continuous, ordered sequence of items. Some examples include sensor data, Internet traffic, financial tickers, on-line auctions, and transaction logs such as Web usage logs and telephone call records. Likewise, queries over streams run continuously over a period of time and incrementally return new results as new data arrives. For example, a temperature detection system of a factory warehouse may run queries like the following.

Query-1: “Every five minutes, retrieve the maximum temperature over the past five minutes.”
Query-2: “Return the average temperature measured on each floor over the past 10 minutes.”

We have developed a Data Stream Management System called Argus, which processes the queries over the data streams. Users can register queries to the Argus. Argus will keep the queries running over the changing data and return the results to the corresponding user with the desired frequency.

For the Argus, we use the following instruction to register a query:

Register Q_num Period

Q_num (0 < Q_num <= 3000) is query ID-number, and Period (0 < Period <= 3000) is the interval between two consecutive returns of the result. After Period seconds of register, the result will be returned for the first time, and after that, the result will be returned every Period seconds.

Here we have several different queries registered in Argus at once. It is confirmed that all the queries have different Q_num. Your task is to tell the first K queries to return the results. If two or more queries are to return the results at the same time, they will return the results one by one in the ascending order of Q_num.

Input
The first part of the input are the register instructions to Argus, one instruction per line. You can assume the number of the instructions will not exceed 1000, and all these instructions are executed at the same time. This part is ended with a line of “#”.

The second part is your task. This part contains only one line, which is one positive integer K (<= 10000).

Output
You should output the Q_num of the first K queries to return the results, one number per line.

Sample Input

Register 2004 200
Register 2005 300
#
5

Sample Output

2004
2005
2004
2004
2005

题目大意:

给出任务的id(各个任务唯一)和执行间隔(各个任务不唯一);要求按照执行的时间顺序来输出要求的钱几个任务id号;当两个任务在同一个时间执行时,先输出id小的;

解题思路:

显然是要求按照执行的时间先后顺序来输出结果;首先考虑排序,发现排序的话,根本不能确定每个任务执行多少次能满足题目要求,所以肯定是不可行的;既 然要求按照时间的先后顺序来输出,那么可以构造优先队列,每次取队头,然后再根据取出任务的执行情况将下一次的执行时间插入到队列里;直到取出满足要求的 个数任务为止。

数据范围:

任务的id号:0—3000;任务时间间隔:0—3000;要求的输出个数:<10000;任务个数: <1000;

边界数据:

基本没有;

ac代码;

#include “stdio.h”
#include “iostream”
using namespace std;
typedef struct _node
{
int id;
int key;
}Node;
Node team[10001] = {0};
int mul[3001];
int totalwant = 0;
int heapsize1 = 0;
//many keywords to sort using qsort~~~
int compare(void const *a, void const *b)
{
if (((Node *)a)->key == ((Node *)b)->key) return 0;
return ((Node *)a)->key > ((Node *)b)->key?1:-1;
}
void increase(int i)
{
int parent = 0;
Node tmp;
parent = i/2;
if ((parent >= 1)&&((team.key < team[parent].key)||(team.key == team[parent].key&&team.id < team[parent].id)))
{
tmp.id = team.id;
tmp.key = team.key;
///
team.id = team[parent].id;
team.key = team[parent].key;
///
team[parent].id = tmp.id;
team[parent].key = tmp.key;
increase(parent);
}
}
void intoheap(int idx, int keyx)
{
heapsize1++;
team[0].key++;
team[heapsize1].id = idx;
team[heapsize1].key = keyx;
increase(heapsize1);
}
void adjust(int i)
{
int l, r, most;
Node tmp;
l = i*2;
r = l + 1;
if ((l <= team[0].key)&&((team[l].key < team.key)||(team[l].key == team.key&&team[l].id < team.id)))
{
most = l;
}
else
{
most = i;
}
if ((r <= team[0].key)&&((team[r].key < team[most].key)||(team[r].key == team[most].key&&team[r].id < team[most].id)))
{
most = r;
}
if (most != i)
{
tmp.id = team[most].id;
tmp.key = team[most].key;
///
team[most].id = team.id;
team[most].key = team.key;
///
team.id = tmp.id;
team.key = tmp.key;
adjust(most);
}
}
void makeheap()
{
for (int i = heapsize1/2; i >= 1; –i)
{
adjust(i);
}
}
Node gettop()
{
Node t;
printf(“%d\n”, team[1].id);
t.id = team[1].id;
t.key = team[1].key;
team[1].id = team[heapsize1].id;
team[1].key = team[heapsize1].key;
heapsize1–;
team[0].key–;
adjust(1);
return t;
}
int main()
{
freopen(“e:\\in.txt”, “r”, stdin);
char temp[10];
int id, key;
int index = 0;
Node getid;
int kk;
for (int i = 0; i < 3001; ++i)
mul = 1;
while(scanf(“%s”, temp))
{
if (temp[0] == ‘#’) break;
index++;
scanf(“%d%d”, &team[index].id, &team[index].key);
}
team[0].key = index;
heapsize1 = index;
scanf(“%d”, &totalwant);
//input the heap members~~~~ and ~~~~~how many they want to output~~~~
//qsort(team, (size_t)index, sizeof(Node), compare);
makeheap();
while(totalwant–)
{
getid = gettop();
kk = mul[getid.id];
mul[getid.id]++;
intoheap(getid.id, (getid.key/kk)*mul[getid.id]);
}
return 0;
}
【可优化】

Incoming search terms for the article: