LETTers练习赛十一场解题报告
第1题解题报告
提交人:蔡驰宇
提交日期:2012.05.09
题目描述
很水的一道题,实际上就是一个三角形求重心的问题,套用公式就好了。
程序
#include<iostream>
#include<cmath>
using namespace std;
const int inf=1<<29;
const int M=50200;
const double eps=1e-8;
struct point
{
double x,y;
}po[5],p,g;
int n;
int main()
{
int T,i;
while(scanf("%d",&T)==1)
{
if(!T) break;
while(T--)
{
for(int i=0;i<3;i++)
scanf("%lf%lf",&po[i].x,&po[i].y);
double tp,area=0,tpx=0,tpy=0;
p.x=po[0].x;
p.y=po[0].y;
for(i=1;i<=3;i++)
{
g.x=po[(i==3)?0:i].x;
g.y=po[(i==3)?0:i].y;
tp=(p.x*g.y-p.y*g.x);
area+=tp/2;
tpx=tpx+(p.x+g.x)*tp;
tpy=tpy+(p.y+g.y)*tp;
p.x=g.x;
p.y=g.y;
}
g.x=tpx/(6*area);
g.y=tpy/(6*area);
printf("%.1lf %.1lf\n",g.x,g.y);
}
}
return 0;
}
第2题解题报告
题目描述
一个二分法的题目,原理并不难,但是在计算时要注意精度,High和Low判断相等的过程中精度要达到10^-9以上才能AC。
程序
#include <iostream>
#include <cmath>
using namespace std;
#define N 10005
#define EPS 1e-10
double elem[N];
int n,m;
int judge(double x)
{
int i;
int count=0;
for(i=0;i<n;i++)
{
count+=(int)(elem[i]/x);
}
if(count>=m)
return 1;
else return 0;
}
int main()
{
int i;
double MAX=0,low,high,mid;
while(scanf("%d%d",&n,&m),m+n)
{
for(i=0;i<n;i++)
{
scanf("%lf",&elem[i]);
MAX+=elem[i];
}
MAX/=m;
low=0;
high=MAX;
while(fabs(low-high)>EPS)
{
mid=(low+high)/2;
if(judge(mid)==1)
low=mid;
else
high=mid;
}
printf("%.2lf/n",low);
}
return 0;
}
第3题解题报告
题目描述
一个较为典型的母函数应用,由题意可知,相同的硬币可以重复,那就为可重复组合。
构造函数G(x)=(1+x+x^2+…)(1+x^4+x^8+…)…(1+x^289+x^578+…)。
程序
#include<stdio.h>
#include<string.h>
#define MAXD 20
#define MAXM 310
int f[MAXD][MAXM], N;
void prepare()
{
int i, j, k;
memset(f, 0, sizeof(f));
f[0][0] = 1;
for(i = 1; i <= 17; i ++)
for(j = 0; j <= 300; j += i * i)
for(k = 0; k + j <= 300; k ++)
f[i][k + j] += f[i - 1][k];
}
int main()
{
prepare();
for(;;)
{
scanf("%d", &N);
if(!N)
break;
printf("%d\n", f[17][N]);
}
return 0;
}
第4题解题报告
题目描述
封锁出口或者入口周围的格子.
最多需要4个封锁点.
所以我们可以采取这样的策略:
1.寻找一条盗贼的可行路线,如果没有,返回0.
2.计算封锁出口和入口四周需要的封锁点数量,取小的一个,假设是k,k <=4
3.从少到多,遍历所有封锁点个数小于k的方案,验证是否是一条有效的覆盖方案
(可以通过是否阻止了1中的盗贼线路进行快速验证).
如果有有效覆盖方案,返回这个方案的覆盖点值,否则继续.
4.如果没有比k小的覆盖方案,返回k.
时间复杂度:
最多(M*N)^3次有效覆盖验证.即(8*8)^3=256k次.其中有很大一部分可以通过快速验证排除(取决于1的路径长短,所以一般1应该求出最短路径的可行路线)
程序
#include <iostream>
#include <queue>
#include <string>
using namespace std;
int n,m,t,ans;
int dir[4][2] = {1,0,-1,0,0,-1,0,1};
char map[10][10];
bool vis[2][10][10];
struct node
{
int x,y,t,k;//x,y为坐标,t为时间,k为是否有碰到宝石
int rox[64],roy[64];//用来保存路径的,每个节点都保存有一条路径,只有到达终点后才有完整的路径
};
node start,end,temp,in;
queue <node> Q;
void dfs(int deep)
{
if(deep > ans) return ;
int i,j,minstep = -1;
node q;
while(!Q.empty())//清空队列
Q.pop();
Q.push(start);//起始位置入队
memset(vis,false,sizeof(vis));//初始化标记数组
vis[0][start.x][start.y] = true;//标记起始点为真
while(!Q.empty())//从起点开始寻找一条路径
{
q = Q.front();
Q.pop();
if(q.t > t)
continue;
if(q.k && map[q.x][q.y] == 'E')//找到出口
{
minstep = q.t;
break;
}
for(i = 0;i < 4;i++)//分别从四个方向开始扫描
{
int xx = q.x + dir[i][0];
int yy = q.y + dir[i][1];
if(xx < 0 || xx >= n || yy < 0 || yy >= m || map[xx][yy] == '#')
continue;//越界或碰到墙
if(map[xx][yy] == 'J')
in.k = 1;//碰到珠宝
else
in.k = q.k;//没有碰到则标记为前一个状态
if(!vis[in.k][xx][yy])
{
vis[in.k][xx][yy] = true;
for(j = 1;j <= q.t;j++) //将前一节点保存的路径加入到现在的节点,由于是按顺序的,所以他会形成连贯的路线
{
in.rox[j] = q.rox[j];
in.roy[j] = q.roy[j];
}
in.x = xx;
in.y = yy;
in.t = q.t + 1;
//这个节点的第in.t步保存的是xx,yy
in.rox[in.t] = xx;
in.roy[in.t] = yy;
Q.push(in);
}
}
}
if(minstep == -1)
{// minstep == -1 表明在t时间内即使不用设置关卡也不能成功逃离
if(deep < ans)
ans = deep;
return ;
}
char cc;
for(i = 1;i < q.t;i++)
{
cc = map[q.rox[i]][q.roy[i]];
if(cc == 'S' || cc == 'E')
continue;
map[q.rox[i]][q.roy[i]] = '#';//在做完前面的bfs后,这里的q是到达终点的节点,因此他完整的保存了一条路
dfs(deep+1);
map[q.rox[i]][q.roy[i]] = cc ;
}
}
void inits()
{
int i,j;
memset(vis,false,sizeof(vis));
for(i = 0;i < n;i++)
for(j = 0;j < m;j++)
{
if(map[i][j] == 'S')
{
start.x = i;start.y = j;
start.t = 0;start.k = 0;
break;
}
}
ans = 4;
dfs(0);
printf("%d\n",ans);
}
int main()
{
int i,cas;
scanf("%d",&cas);
while(cas--)
{
scanf("%d%d%d",&n,&m,&t);
for(i = 0;i < n;i++)
scanf("%s",map[i]);
inits();
}
return 0;
}