Greater New York Regional 2009 解题报告

Greater New York Regional 2009 解题报告

寒假没事的时候看了下,顺手做了。。。果断AK掉了。。。
blog在chengdu regional后就一直没更新了,在被补考前憋的头昏脑胀无力吐槽的时候,就当来清清草吧。。。

这套水题就懒得每题单独发了。。。

A题 poj 3781 Nth Largest Value
无与伦比的水,1分钟AC不了可以去shi了。。。我直接扫描三次水过。。

#include <stdio.h>

int ncase;

int main()
{
    scanf(
"%d",&ncase);
    
for ( int icase = 0 ; icase < ncase ; icase++ )
    {
        
int sets, num[15];
        scanf(
"%d"&sets);
        
for ( int i = 0 ; i < 10 ; i++ )
            scanf(
"%d", num+i );
        
for ( int m = 0 ; m < 3 ; m++ )
        {
            
int max = num[0], index = 0;
            
for ( int i = 1 ; i < 10 ; i++ )
                
if ( num[i] > max )
                {
                    max 
= num[i];
                    index 
= i;
                }
            
if ( m == 2 )
                printf(
"%d %d\n", sets, max);
            
else
                num[index] 
= 0;
        }
    }
    
return 0;
}

B题 poj 3782 Equal Sum Partitions
直接DFS过掉了

#include <stdio.h>

int ncase;
int num[10100];

bool judge(int sums, int m, int numbers)
{
    
int sum = 0;
    
for ( int i = m+1 ; i < numbers ; i++ )
    {
        
if ( sum < sums )
            sum 
+= num[i];
        
else if ( sum == sums )
            sum 
= num[i];
        
else return 0;
    }
    
if ( sum == sums )
        
return 1;
    
else return 0;
}

int main()
{
    scanf(
"%d"&ncase);
    
for ( int icase = 0 ; icase < ncase ; icase++ )
    {
        
int sets, numbers;
        scanf(
"%d%d"&sets, &numbers);
        
for ( int i = 0 ; i < numbers ; i++ )
            scanf(
"%d", num+i);
        
int sum = 0;
        
for ( int i = 0 ; i < numbers ; i++ )
        {
            sum 
+= num[i];
            
if ( judge(sum, i, numbers) )
                
break;
        }
        printf(
"%d %d\n", sets, sum);
    }
    
return 0;
}

C题 poj 3783 Balls
经典的鹰蛋,dp,在蛋的数目超过logN后直接找前面的答案就行了(我只用了这个优化居然16MS水过去了)。。
更多参见OI论文04年   朱晨光:《优化,再优化!——从《鹰蛋》一题浅析对动态规划算法的优化》

#include <stdio.h>
#include 
<math.h>

#define min(a,b) (a)>(b)?(b):(a)
#define max(a,b) (a)>(b)?(a):(b)

int dp[60][1010];

int main()
{
    
for ( int i = 0 ; i <= 1000 ; i++ )
        dp[
1][i]=i;
    
for ( int i = 0 ; i <= 50 ; i++ )
    {
        dp[i][
0]=0;
        dp[i][
1]=1;
    }
    
for ( int j = 2 ; j <= 1000 ; j++ )
    {
        
int loor = (int)(floor(log(j+0.0)/log(2.0))+1.0);
        
for ( int i = 2 ; i <= loor ; i++ )
        {
            dp[i][j]
=(max(dp[i-1][0], dp[i][j-1]))+1;
            
for ( int k = 2 ; k <= j ; k++ )
            {
                dp[i][j]
=min(dp[i][j], (max(dp[i-1][k-1], dp[i][j-k]))+1);
            }
        }
        
for ( int i = loor+1 ; i <= 50 ; i++ )
            dp[i][j]
=dp[loor][j];
    }
    
int n, sets, b, m;
    scanf(
"%d"&n);
    
while ( n-- )
    {
        scanf(
"%d%d%d"&sets, &b, &m);
        printf(
"%d %d\n", sets, dp[b][m]);
    }
    
return 0;
}

D题 poj 3784 Running Median
先加入一个数并输出,以后每次加入两个数,然后输出中位数
可以用平衡树做,不过这种水题上平衡树就太没事找事了,维护最大,最小两个堆过了(写的太挫了,快90行了)

#include <cstdio>
#include 
<algorithm>
#include 
<cstring>

using namespace std;

int ncase;

bool cmpb(int a, int b)
{
    
return a>b;
}

bool cmps(int a, int b)
{
    
return a<b;
}

int main()
{
    scanf(
"%d"&ncase);
    
for ( int icase = 0 ; icase < ncase ; icase++ )
    {
        
int sets, n, count = 0;
        
int max[10100], min[10100], maxn = 1, minn = 0;
        memset(max, 
0sizeof(max));
        memset(min, 
0sizeof(min));
        scanf(
"%d%d"&sets, &n);
        printf(
"%d %d\n", sets, (n+1)>>1);
        
if (n)
        {
            scanf(
"%d", max);
            printf(
"%d", max[0]);
        }
        
for ( int i = 1 ; i < n ; i+=2 )
        {
            
int a, b;
            scanf(
"%d%d"&a, &b);
            
if ( a > max[0] )
            {
                max[maxn
++]=a;
                push_heap(max, max
+maxn, cmpb);
            }
            
else
            {
                min[minn
++]=a;
                push_heap(min, min
+minn, cmps);
            }
            
if ( b > max[0] )
            {
                max[maxn
++]=b;
                push_heap(max, max
+maxn, cmpb);
            }
            
else
            {
                min[minn
++]=b;
                push_heap(min, min
+minn, cmps);
            }
            
while ( maxn > minn+1 )
            {
                min[minn
++= max[0];
                pop_heap(max, max
+maxn, cmpb);
                push_heap(min, min
+minn, cmps);
                maxn
--;
            }
            
while ( maxn < minn+1 )
            {
                max[maxn
++= min[0];
                pop_heap(min, min
+minn, cmps);
                push_heap(max, max
+maxn, cmpb);
                minn
--;
            }
            
if ( count >= 9 )
            {
                putchar(
10);
                count 
= 0;
                printf(
"%d", max[0]);
            }
            
else
            {
                count
++;
                printf(
" %d", max[0]);
            }
        }
        putchar(
10);
    }
    
return 0;
}

E题 poj 3785 The Next Permutation
顾名思义,求下一个排列,直接用algorithm里的next_permutation水过。。。

#include <algorithm>
#include 
<cstdio>
#include 
<cstring>

using namespace std;

int ncase;

int main()
{
    scanf(
"%d"&ncase);
    
for ( int icase = 0 ; icase < ncase ; icase++ )
    {
        
int tcase;
        
char str[100];
        scanf(
"%d%s"&tcase, str);
        
if ( next_permutation(str, str+strlen(str)) )
            printf(
"%d %s\n", tcase, str);
        
else printf("%d BIGGEST\n", tcase);
    }
    
return 0;
}

F题 poj 3786 Adjacent Bit Counts
同样水题,dp水过了。。
状态转移:
dp[i][j][1]=dp[i-1][j][0]+dp[i-1][j-1][1];
dp[i][j][0]=dp[i-1][j][1]+dp[i-1][j][0];
就是当前dp[i][j]为1,则可能数是dp[i-1][j][0]+dp[i-1][j-1][1]的可能的数目
当前dp[i][j]为0,则可能数是dp[i-1][j][1]+dp[i-1][j][0]的可能的数目
注意下j=0时的初始化的问题就OK了

#include <stdio.h>

unsigned 
int dp[110][110][2];

int main()
{
    dp[
1][0][0]=dp[1][0][1]=1;
    
for ( int i = 2 ; i <= 100 ; i++ )
    {
        dp[i][
0][0]=dp[i-1][0][1]+dp[i-1][0][0];
        dp[i][
0][1]=dp[i-1][0][0];
    }
    
for ( int j = 1 ; j < 100 ; j++ )
        
for ( int i = 2 ; i <= 100 ; i++ )
        {
            dp[i][j][
1]=dp[i-1][j][0]+dp[i-1][j-1][1];
            dp[i][j][
0]=dp[i-1][j][1]+dp[i-1][j][0];
        }
    
int ncase, sets, m, n;
    scanf(
"%d"&ncase);
    
while ( ncase-- )
    {
        scanf(
"%d%d%d"&sets, &m, &n);
        printf(
"%d %d\n", sets, dp[m][n][0]+dp[m][n][1]);
    }
    
return 0;
}

G题 poj 3787 Convex Hull of Lattice Points
模板题,输出凸包的序列,注意下顺序就OK(不过做这题的意外收获是居然查到了我模板的一个bug~(*^__^*) 嘻嘻。。当然是我自己的模板太挫了的原因。。。)

#include <stdio.h>
#include 
<math.h>
#include 
<stdlib.h>

const double eps=1e-8;
const int N=60;

struct Point
{
    
int x, y;
}point[N];

int top, stack[N];
int n, ncase, sets;

inline 
int cross(Point a, Point b, Point c)
{
    
return (c.x-a.x)*(b.y-a.y) - (b.x-a.x)*(c.y-a.y);
}

inline 
double dis(Point a, Point b)
{
    
return sqrt( (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y) );
}

int cmp1(const void *a, const void *b)
{
    
int t= cross(point[0], *(Point *)a, *(Point *)b);
    
if (!t)
        
return dis(point[0],*(Point *)a)-dis(point[0],*(Point *)b)+eps;
    
else
        
return t;
}

int cmp(const void *a, const void *b)
{
    
if ( (*(Point *)a).y == (*(Point *)b).y )
        
return (*(Point *)a).x - (*(Point *)b).x;
    
return (*(Point *)b).y - (*(Point *)a).y;
}

void graham()
{
    qsort(point, n, 
sizeof(Point), cmp);
    qsort(point
+1, n-1sizeof(Point), cmp1);
    stack[
0]=0, stack[top=1]=1;
    
for ( int i = 2 ; i < n ; i++ )
    {
        
while(cross(point[stack[top]],point[stack[top-1]],point[i])<=0&&top!=0)
            top
--;
        stack[
++top]= i;
    }
    top
++;
}

int main()
{
    scanf(
"%d"&ncase);
    
while ( ncase-- )
    {
        scanf(
"%d%d"&sets, &n);
        
for ( int i = 0 ; i < n ; i++ )
            scanf(
"%d%d"&point[i].x, &point[i].y);
        graham();
        printf(
"%d %d\n", sets, top);
        printf(
"%d %d\n", point[stack[0]].x, point[stack[0]].y);
        
for ( int i = top-1 ; i > 0 ; i-- )
            printf(
"%d %d\n", point[stack[i]].x, point[stack[i]].y);
    }
    
return 0;
}
PS:私以为坐标为int的时候叉乘用int足矣~

H题 poj 3788 Interior Points of Lattice Polygons
同样是类似凸包的题,只不过给你一个凸包然后求里面的个数(必须是严格的里面,在线上的不算),直接记录下上下左右的最大值,在后用叉乘暴力就OK~

#include <stdio.h>

const int MAXN=~0u>>1;

struct Point
{
    
int x, y;
};

int ncase, n, sets;
Point point[
60];

inline 
int max(int a, int b)
{
    
return a>b?a:b;
}

inline 
int min(int a, int b)
{
    
return a<b?a:b;
}

inline 
int cross(Point a, Point b, Point c)
{
    
return (c.x-a.x)*(b.y-a.y) - (b.x-a.x)*(c.y-a.y);
}

inline 
bool dd(Point a, Point b)
{
    
return a.x==b.x&&a.y==b.y;
}

bool judge(int y, int x, int n)
{
    Point t;
    t.x
=x, t.y=y;
    
for ( int i = 0 ; i < n ; i++ )
        
if ( dd(t,point[i]) || dd(t,point[(i+1)%n]) || cross(t, point[(i+1)%n], point[i]) >= 0 )
            
return 0;
    
return 1;
}

int main()
{
    scanf(
"%d"&ncase);
    
while ( ncase-- )
    {
        scanf(
"%d%d"&sets, &n);
        
for ( int i = 0 ; i < n ; i++ )
            scanf(
"%d%d"&point[i].x, &point[i].y);
        
int top=-MAXN, button=MAXN, left=MAXN, right=-MAXN;
        
for ( int i = 0 ; i < n ; i++ )
        {
            top
=max(top, point[i].y);
            button
=min(button, point[i].y);
            right
=max(right, point[i].x);
            left
=min(left, point[i].x);
        }
        Point line[
4000], m={1,0};
        
int count=0;
        
for ( int i = button+1, flag = 1; i < top ; i++ )
        {
            line[i
+2000].x=MAXN;
            line[i
+2000].y=-MAXN;
            
for ( int j = left+1 ; j < right ; j++ )
                
if ( judge(i, j, n) )
                {
                    line[i
+2000].x=min(line[i+2000].x, j);
                    line[i
+2000].y=max(line[i+2000].y, j);
                }
            
if (line[i+2000].x!=MAXN)
            {
                count
++;
                
if ( flag)
                    m.x 
= i, flag = 0;
                
if ( !flag)
                    m.y 
= i;
            }
        }
        printf(
"%d %d\n", sets, count);
        
for ( int i = m.y ; i >= m.x ; i-- )
            
if ( line[i+2000].x!=MAXN )
                printf(
"%d %d %d\n", i, line[i+2000].x, line[i+2000].y);
    }
    
return 0;
}

整套题都感觉灰常的水,除了鹰蛋那题用了点小优化以外,没啥技术含量,但这么简单的一套题居然没啥人去切= =囧~
PS:鹰蛋那题的OI论文太强大了,优化到了sqrt(n),太神了,借此机会膜拜下神牛。。。

你可能感兴趣的:(Greater New York Regional 2009 解题报告)