寒假没事的时候看了下,顺手做了。。。果断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, 0, sizeof(max));
memset(min, 0, sizeof(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-1, sizeof(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),太神了,借此机会膜拜下神牛。。。