福州大学第十二届程序设计竞赛 (部分题解)

比赛链接:http://acm.fzu.edu.cn/contest/list.php?cid=144


 Problem B 完美的数字
Time Limit: 1000 mSec    Memory Limit : 32768 KB

Problem Description

Bob是个很喜欢数字的孩子,现在他正在研究一个与数字相关的题目,我们知道一个数字的完美度是 把这个数字分解成三个整数相乘A*A*B(0<A<=B)的方法数,例如数字80可以分解成1*1*80,2*2*20 ,4*4*5,所以80的完美度是3;数字5只有一种分解方法1*1*5,所以完美度是1,假设数字x的完美度为d(x),现在给定a,b(a<=b),请你帮Bob求出

S,S表示的是从a到b的所有数字的流行度之和,即S=d(a)+d(a+1)+…+d(b)。

Input

输入两个整数a,b(1<=a<=b<=10^15)

Output

输出一个整数,表示从a到b的所有数字流行度之和。

Sample Input

1 80

Sample Output

107

题目分析:枚举到B^(1/3)即可,首先每个数字的完美度至少为1,因为x/(i*i)  (i >= 2)必须大于i才满足A*A*B(0<A<=B),所以对于每个可能的枚举值的额外完美度为x/(i*i)-i+1


#include <cstdio>
#include <cstring>
#define ll long long

ll cal(ll x)
{
	ll cnt = 0;
	for(ll i = 2; i * i * i <= x; i++)
		cnt += x / (i * i) - i + 1;
	return x + cnt;
}

int main()
{
	ll a, b;
	while(scanf("%I64d %I64d", &a, &b) != EOF)
		printf("%I64d\n", cal(b) - cal(a - 1));
}



Problem D So Hard
Time Limit: 1000 mSec    Memory Limit : 32768 KB


Problem Description

请将有限小数化为最简分数。

Input

一个整数n 表示需要转化的小数个数;接下来n行,每行有一个有限小数。(保证小数位数不超过9位)

Output

输出有n行,每行为小数对应的最简分数

Sample Input

2
0.5
0.4

Sample Output

1/2
2/5

Hint

注意精度问题,数据保证不会有类似1.0的小数。

题目分析:字符串读,分子分母同除他们的最大公约数


#include <cstdio>
#include <cstring>
#define ll long long
int const MAX = 1e5;
char s[MAX];

ll gcd(int a, int b)
{
	return b ? gcd(b, a % b) : a;  
}

int main()
{
	int T;
	scanf("%d", &T);
	while(T--)
	{
		scanf("%s", s);
		int l = strlen(s);
		ll len = 1;
		ll num = 0;
		bool flag = false;
		for(int i = 0; i < l; i++)
		{
			if(s[i] == '.')
			{
				flag = true;
				continue;
			}
			num = num * 10 + (s[i] - '0');
			if(flag)
				len *= 10;
		}
		ll g = gcd(num, len);
		printf("%I64d/%I64d\n", num / g, len / g);
	}
}



Problem F 检查站点
Time Limit: 1000 mSec    Memory Limit : 32768 KB

Problem Description

在山上一共有N个站点需要检查,检查员从山顶出发去各个站点进行检查,各个站点间有且仅有一条通路,检查员下山前往站点时比较轻松,而上山时却需要额外的时间,问最后检查员检查完所有站点时所需要的额外时间最少是多少。

Input

包含多组数据每组数据输入第一行为一个整数N 表示站点个数(1<=N<=100000),接下去N-1 行 每行3个整数 x,y,z(1<=z<=10000) 检查站x为检查站y的父节点,x,y之间有一条通路,从y到x需要额外z的时间。(父节点在子节点上方,山顶固定标号为1)

Output

输出一行一个整数表示最少需要花费的额外时间。

Sample Input

6
1 2 1
2 4 1
1 3 1
3 5 1
3 6 1

Sample Output

3

题目分析:全部值减去权值最大的一条从根到叶子的路径值即可

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int const MAX = 1e5 + 5;
int re[MAX], fa[MAX];
int n;

int get_num(int v)
{
	int cnt = 0;
	for(; v; v = fa[v])
		cnt += re[v];
	return cnt;
}

int main()
{
	while(scanf("%d", &n) != EOF)
	{
		memset(re, 0, sizeof(re));
		memset(fa, 0, sizeof(fa));
		int sum = 0;
		for(int i = 0; i < n - 1; i++)
		{
			int u, v, w;
			scanf("%d %d %d", &u, &v, &w);
			fa[v] = u;
			re[v] = w;
			sum += w;
		}
		int ma = 0;
		for(int i = 2; i <= n; i++)
			ma = max(ma, get_num(i));
		printf("%d\n", sum - ma);
	}
}</span>


Problem G Escape
Time Limit: 1000 mSec    Memory Limit : 32768 KB

Problem Description

小明进入地下迷宫寻找宝藏,找到宝藏后却发生地震,迷宫各处产生岩浆,小明急忙向出口处逃跑。如果丢下宝藏,小明就能迅速离开迷宫,但小明并不想轻易放弃自己的辛苦所得。所以他急忙联系当程序员的朋友你(当然是用手机联系),并告诉你他所面临的情况,希望你能告诉他是否能成功带着宝藏逃脱。

Input

有多组测试数据。

每组测试数据第一行是一个整数T,代表接下去的例子数。(0<=T<=10)

接下来是T组例子。

每组例子第一行是两个整数N和M。代表迷宫的大小有N行M列(0<=N,M<=1000)。

接下来是一个N*M的迷宫描述。

S代表小明的所在地。

E代表出口,出口只有一个。

.代表可以行走的地方。

!代表岩浆的产生地。(这样的地方会有多个,其个数小于等于10000)

#代表迷宫中的墙,其不仅能阻挡小明前进也能阻挡岩浆的蔓延。

小明携带者宝藏每秒只能向周围移动一格,小明不能碰触到岩浆(小明不能和岩浆处在同一格)。

岩浆每秒会向四周不是墙的地方蔓延一格。

小明先移动完成后,岩浆才会蔓延到对应的格子里。

小明能移动到出口,则小明顺利逃脱。

Output

每组测试数据输出只有一行“Yes”或者“No”。“Yes”代表小明可以成功逃脱。否则输出“No”。

Sample Input

3
5 5
....!
S....
#....
!#...
#E...
2 2
S.
!E
2 2
SE
!.

Sample Output

Yes
No
Yes

题目分析:BFS1预处理各个点岩浆到达的最短时间,BFS2搜人是否能出去,要注意的一点是如果人和岩浆同时到E,则人算成功逃脱


#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
int const MAX = 1005;
int const INF = 0x7fffffff;
char map[MAX][MAX];
int tim[MAX][MAX];
bool vis[MAX][MAX];
int n, m;
int sx, sy, ex, ey;
int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, -1, 0, 1};

struct NODE
{
	int x, y;
	int step;
};

bool ok(int i, int j)
{
	if(i >= 0 && j >= 0 && i < n && j < m)
		return true;
	return false;
}

void BFS1()
{	
	NODE yj;
	queue <NODE> q;
	for(int i = 0; i < n; i++)
	{
		for(int j = 0; j < m; j++)
		{
			if(map[i][j] == '!')
			{
				yj.x = i;
				yj.y = j;
				yj.step = 0;
				tim[i][j] = 0;
				q.push(yj);
			}
		}
	}
	while(!q.empty())
	{
		NODE now = q.front(), t;
		q.pop();
		//printf("now.x = %d  now.y = %d  now.step = %d\n", now.x, now.y, now.step);
		for(int i = 0; i < 4; i ++)
		{
			t.x = now.x + dx[i];
			t.y = now.y + dy[i];
			t.step = now.step + 1;
			if(ok(t.x, t.y) && t.step < tim[t.x][t.y])
			{
				tim[t.x][t.y] = t.step;
				q.push(t);
			}
		}
	}
	return;
}

bool BFS2()
{	
	memset(vis, false, sizeof(vis));
	NODE st;
	st.x = sx;
	st.y = sy;
	vis[sx][sy] = true;
	st.step = 0;
	queue <NODE> qq;
	qq.push(st);
	while(!qq.empty())
	{
		NODE now = qq.front(), t;
		qq.pop();
		if(now.x == ex && now.y == ey && t.step <= tim[t.x][t.y])
			return true;
		for(int i = 0; i < 4; i++)
		{
			t.x = now.x + dx[i];
			t.y = now.y + dy[i];
			t.step = now.step + 1;
			if(t.x == ex && t.y == ey && t.step <= tim[t.x][t.y])
				return true;
			if(ok(t.x, t.y) && !vis[t.x][t.y] && t.step < tim[t.x][t.y])
			{
				vis[t.x][t.y] = true;
				qq.push(t);
			}
		}
	}
	return false;
}

int main()
{
	int T;
	while(scanf("%d", &T) != EOF)
	{
		while(T--)
		{
			scanf("%d %d", &n, &m);
			if(n == 0 || m == 0)
			{
				printf("No\n");
				continue;
			}
			for(int i = 0; i < n; i++)
			{
				scanf("%s", map[i]);
				for(int j = 0; j < m; j++)
				{
					tim[i][j] = INF;
					if(map[i][j] == 'S')
					{
						sx = i;
						sy = j;
					}
					if(map[i][j] == 'E')
					{
						ex = i;
						ey = j;
					}
					if(map[i][j] == '#')
						tim[i][j] = 0;
				}
			}
			BFS1();
			printf("%s\n", BFS2() ? "Yes" : "No");
		}
	}
}



Problem 2197 最小花费
Time Limit: 1000 mSec    Memory Limit : 32768 KB

Problem Description

给一个长度为n(n <= 10^5)的“01”串,你可以任意交换一个为0的位和一个为1的位,若这两位相邻,花费为X,否则花费为Y。求通过若干次交换后将串中的“1”全部变换到“0”前面的最小花费。

Input

第一行一个整数T(1 <= T <= 10),表示测试数据的组数。接下来3*T行,每组数据三行,第一行为整数X(1 <= X <= 10^3),第二行为整数Y(X <= Y <= 10^3),第三行是“01”串。

Output

最小花费。

Sample Input

2
1
2
1100
1
2
0011

Sample Output

0
3

题目分析:两种策略,跨着换,相邻换,首先相邻换的花费是固定的,所以一个1和一个0,要交换的花费为min(y, x * len)
先处理处所有1的位置和个数cnt,我们要让前cnt均为1,则从0到cnt-1枚举,遇到一个0则拿最后的一个1和它交换

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int const MAX = 1e5;
char s[MAX];
int idx[MAX];

int main()
{
	int T;
	scanf("%d", &T);
	while(T--)
	{
		int x, y;
		scanf("%d %d",&x, &y);
		scanf("%s", s);
		int len = strlen(s), cnt = 0;
		for(int i = len - 1; i >= 0; i--)
			if(s[i] == '1')
				idx[cnt ++] = i;
		int ans = 0, now = 0;
		for(int i = 0; i < cnt; i++)
			if(s[i] == '0')
				ans += min(y, x * (idx[now ++] - i));
		printf("%d\n", ans);
	}
}





评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值