游记
Morning
【Before 8:00】 依然起得很晚(出了艾蕾好开桑),去饭堂豆浆都是凉的。在课室习惯性的开了电脑,却发现电脑本来就是开的(昨晚没有关),所以我成功的把电脑关机了。打开电脑,看了看C组的比赛。
题目 | 做法 |
---|---|
立体井字棋 | 数论 |
最小花费 | 最短路(Dijkstra) |
最大约数和 | DP、递推 |
excel 电子表格 | 模拟 |
【Before 12:00】 OJ依旧卡得要死,半个小时前的程序丝毫没有评测的迹象。几面程序都显示正在评测,还有一些人开了地球。
得分:100+0+100+0=200分(其实只做了这两题)
大佬+蒟蒻本人排行榜
姓名 | 得分 | 名次 |
---|---|---|
PRJ | 310 | 1 |
LYF | 300 | 2 |
蒟蒻本人 | 200 | 7 |
CNH | 200 | 7 |
我依旧很弱 (滑稽)
Afternoon
【Before 14:00】 终于睡了一个中午的饭,MOM说今天下午走嘤嘤嘤。。。
【Before 16:00】402教室讲课中。。。难度:第一题<第三题<第四题<第二题
Evening
【19:00】哎~~终于要走了吗
题解
T1立体井字棋
题目描述
你玩过井字棋游戏吗?它的英文名字叫做tic-tac-toe,是一个古老的博弈游戏。游戏在一个3 £ 3的棋盘上进行。游戏约定,先在同一条线(横线、纵线或斜线)上占有3枚棋子者得胜。尽管现在借助计算机可以生成这个游戏所有可能的情况,但这个古老的游戏从未失去它在博弈游戏中的意义。在它的身上不断可以看到有新的东西产生。比如,有人将井字棋游戏从平面空间扩展到三维空间,发明了立体井字棋。立体井字棋的棋盘是一个n £ n £ n的立方体,游戏双方在立方体的这n3个格子中布子。与传统井字棋游戏的规则相似,首先占据了一条线上的全部n个格子的人获胜。当然,这个立方体的大小是有考究的,并不是所有的正整数n都合适: n小了获胜太易,先行者必胜; n大了获胜又太难,最后可能双方都无法获胜。为此,我们需要收集与该游戏有关的一些数据,以决定最佳的n的值。我们想知道,对于某个n,在游戏中有多少种获胜的情况。你的任务是确定,在n £ n £ n的立方体中放n个子,有多少种布子方案可以使这n个子连成一条线。
输入
输入数据为一个正整数n,表示立方体的大小。
输出
输出为一个正整数,它表示在n3的立方体中n个格子连成一条直线的方案数。
样例输入
2
样例输出
28
数据范围限制
对于30%的数据, n<=10;
对于100%的数据, n <= 1000。
【难度系数】★☆☆☆☆
【核心算法】数论
【思路】
①假设有一个大小为(1+n+1 ) “的立方体“包住了”这个大小为n的立方体。假如这个边长为n 的立方体中的每一条直线都“往两头延伸”,那么每一条线都会“穿透”外面更大的立方体中的两个单位立方体。于是,我们只需要计算出护的立方体“外面的一层壳”有多少个单位立方体就行了。显然,壳子有(n + 2 ) “一矿个单位立方体。这个数除以2 即是我们要求的答案。
②公式为(n+2)×(n+2)×(n+2)-n×n×n)/2
=(n×n×n+4×n×n+4×n+2×n×n+8×n+8-n×n×n)/2
=(6×n×n+12×n+8)/2
=3×n×n+6×n+4
③没有了喵
代码
#include<iostream>
#include<cstdio>
using namespace std;
int n;
int main(){
cin>>n;
if (n==1) cout<<1<<endl;
else cout<<3*n*n+6*n+4;
}
T2最小花费
题目描述
在n个人中,某些人的银行账号之间可以互相转账。这些人之间转账的手续费各不相同。给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元。
输入
第一行输入两个用空格隔开的正整数n和m,分别表示总人数和可以互相转账的人的对数。以下m行每行输入三个用空格隔开的正整数x; y; z,表示标号为x的人和标号为y的人之间互相转账需要扣除z%的手续费(z < 100)。最后一行输入两个用空格隔开的正整数A和B。数据保证A与B之间可以直接或间接地转账。
输出
输出A使得B到账100元最少需要的总费用。精确到小数点后8位。
样例输入
3 3
1 2 1
2 3 2
1 3 3
1 3
样例输出
103.07153164
数据范围限制
对于所有数据, 1 <= n <=2000。
【难度系数】★★☆☆☆
【核心算法】最短路(Dijkstra)
【思路】
①某个人X 直接给另一个人Y 转账后,假如Y 收到了亡元钱,手续费为:% ,那么X 花费了亡/( 1 一:% )元钱。假如X 和Y 之间可以直接转账且手续费为:%的话,我们连接一条边并赋权值为l / ( 1 一:% )。为了计算A 最少需要花费多少钱,我们需要找到一条路,使得B 到A 走过的路的权值乘积最小。由于权值都是大于1 的数(总是越乘越大),因此我们可以用Dijks 七ra 算法。至于权值乘积最小为什么也能用最短路算法,这可以用Dijkstra 算法的原理来解释,这里不再叙述。
②我们用Dijkstra的方法,先将(100-税率)/100求出汇率(应该是汇率),然后从x开始向其他点延伸,但要注意他要使汇率最大,所以应该是说最长路,最后用100/结果
代码
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,x,y,l;
double f[2002][2002],b[2002],t,d;
bool c[2002];
int main()
{
scanf("%d %d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d %d %lf",&x,&y,&d);//输入
f[x][y]=f[y][x]=(100-d)/100;//求出汇率
}
scanf("%d %d",&x,&y);
for (int i=1;i<=n;i++)
b[i]=f[x][i];//从第一个人延伸
b[x]=1;//预处理
c[x]=true;//记录
for (int i=2;i<n;i++)
{
t=0;//清空
l=0;//清空
for (int j=1;j<=n;j++)
if ((!c[j])&&(b[j]>t))//判断是否是汇率最大的
{
t=b[j];//代替
l=j;//记录
}
if (!l) break;//若无最大的,就退出
c[l]=true;//记录
for (int j=1;j<=n;j++)
if ((!c[j])&&(b[l]*f[l][j]>b[j]))//判断是否更优
b[j]=b[l]*f[l][j];//代替
}
printf("%.8lf",100/b[y]);//结果
}
T3最大约数和
题目描述
选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。
输入
输入一个正整数S。
输出
输出最大的约数之和。
样例输入
11
样例输出
9
数据范围限制
对于30%的数据, S<=10;
对于100%的数据, S <=1000。
提示
样例说明:
取数字4和6,可以得到最大值(1 + 2) + (1 + 2 + 3) = 9。
【难度系数】★★☆☆☆
【核心算法】DP/递推
【思路】这个题目的算法设计得很隐蔽。它故意误导人们去想数论方面的相关知识(虽然数论知识有可能解决)。其实这道题就是最简单的01 背包问题。选的数值就是物品的重量,背包的总载重就是s ,每个数的约数和就是物品的价值。题目就是一个很基本的动态规划。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int s,i,j,f[10001]={0};
int main()
{
scanf("%d",&s);
f[1]=0;
for (i=2;i<=s;i++)
for (j=1;j<=i/2;j++) if (i%j==0) f[i]+=j;
for (i=2;i<=s;i++)
for (j=1;j<=i/2;j++) f[i]=max(f[i],f[j]+f[i-j]);
printf("%d",f[s]);
return 0;
}
T4电子表格
题目描述
也许你用过Microsoft Excel之类的电子制表软件,这类软件最令人称道的就是强大的公式计算功能。现在希望你也来实现一个具有最基本功能的电子制表软件。表格共有m列(0 < m • 26),从左到右依次用A到Z的大写英文字母表示;有n行(0 < n <100),从上到下依次用1到100的整数表示。这样,每一个单元格的位置就可以唯一地用它所在的列和行表示出来,例如从左到右第3列,从上到下第5行的单元格就可以用“ C5”来表示(注意,这里字母和数字中间没有空格)。
现在对表格进行了一系列的操作,这些操作主要就是赋值和查询。定义操作的输入规则
如下:
1.每个操作占一行,根据操作类型的不同,每行中可能有二至四个用空格隔开的“单词”;
2.每行的第一个单词指定了该操作涉及的单元格的位置;
3.每行的第二个单词指定了相应的操作,可能是: input,output,sum,avg
(1).如果第二个单词是input,表示接下来的一个整数是要赋予该单元格的值,这个值是
不超过1000的正整数
(2).如果第二个单词是output,表示你需要在输出文件中输出这个单元格当前的值
(3).如果第二个单词是sum,表示接下来输入的两个单词定义了一个矩形区域,该单元格的值就应该恒为这个矩形区域中所包含的单元格的值的和,直到该单元格被重新定义
(4).如果第二个单词是avg,表示接下来输入的两个单词定义了一个矩形区域,该单元格的值就应该恒为这个矩形区域中所包含的单元格的值的算术平均数,直到该单元格被重新定义;
4.“输入的两个单词定义了一个矩形区域”是指输入一个矩形区域的左上角和右下角的单元格的位置,这样就唯一确定了这个矩形区域;
5.所有时刻,每个单元格的值均为整数,如果不是,则向下取整;
6.如果某个单元格的值没有在上文定义,则它的值默认为0;
7.不会出现循环定义的情况;
8.在操作过程中所有单元格的值不超过231-1。
输入
第一行输入两个用空格隔开的正整数m和n,分别代表表格的列数和行数。
第二行输入一个正整数s,表示操作的总数。
以下s行每行输入一个操作,具体格式参见问题描述。
输出
对于输入数据的每一个“ output”操作输出一行结果。因此,输出文件的行数等于输入文
件中“ output”操作的个数。
样例输入
3 5
5
A1 input 100
B2 input 200
C3 sum A1 C2
C5 avg B2 C4
C5 output
样例输出
83
数据范围限制
对于30%的数据, m; n; s <= 10;
对于100%的数据, m <= 26, n < =100, s <=100。
【难度系数】★☆☆☆☆
【核心算法】模拟
【思路】每次有新的定义时直接计算该单元格的值是错误的,因为sum 和avg 的单元格必须随时更新(题目说明了这样的单元格必须恒等于要求的数值和或平均数)。我的方法是,每次有新的单元格被定义后,递归地计算出每个格子的值;如果计算过程中遇到某个由sum 或avg 定义的单元格,递归地调用它。储存下已经计算出的格子的值(相当于记忆化搜索)可以防止重复运算,提高效率。由于不会出现循环定义,因此该递归过程总有结束的时候。
代码
var
n,m,i,j,k,l,t1,t2,t3,t4,t5,t6,p:longint;
a:array[1..100,1..100,1..5]of longint;
s:string;
function sum(x,y:longint):longint;
var
i,j:longint;
begin
if a[x,y,5]=0 then exit(0);
if a[x,y,5]=1 then exit(a[x,y,1]);
sum:=0;
for i:=a[x,y,1] to a[x,y,3] do
for j:=a[x,y,2] to a[x,y,4] do
sum:=sum+sum(i,j);
if a[x,y,5]=2 then exit(sum) else exit(sum div ((a[x,y,3]-a[x,y,1]+1)*(a[x,y,4]-a[x,y,2]+1)));
end;
procedure dy1;
begin
delete(s,1,6);
val(s,p);
fillchar(a[t1,t2],sizeof(a[t1,t2]),0);
a[t1,t2,5]:=1;
a[t1,t2,1]:=p;
end;
procedure dy2;
begin
a[t1,t2,5]:=2;
delete(s,1,4);
t4:=ord(s[1])-64;
for i:=1 to length(s) do
if s[i]=' ' then
begin
j:=i;
break;
end;
val(copy(s,2,j-2),t3);
delete(s,1,j);
t6:=ord(s[1])-64;
delete(s,1,1);
val(s,t5);
a[t1,t2,1]:=t3;
a[t1,t2,2]:=t4;
a[t1,t2,3]:=t5;
a[t1,t2,4]:=t6;
end;
procedure dy3;
begin
a[t1,t2,5]:=3;
delete(s,1,4);
t4:=ord(s[1])-64;
for i:=1 to length(s) do
if s[i]=' ' then
begin
j:=i;
break;
end;
val(copy(s,2,j-2),t3);
delete(s,1,j);
t6:=ord(s[1])-64;
delete(s,1,1);
val(s,t5);
a[t1,t2,1]:=t3;
a[t1,t2,2]:=t4;
a[t1,t2,3]:=t5;
a[t1,t2,4]:=t6;
end;
procedure dy4;
begin
if a[t1,t2,5]=1 then writeln(a[t1,t2,1])
else
if (a[t1,t2,5]=2)or(a[t1,t2,5]=3) then writeln(sum(t1,t2))
else writeln(0);
end;
begin
readln(m,n);
readln(l);
for k:=1 to l do
begin
readln(s);
t2:=ord(s[1])-64;
for i:=1 to length(s) do
if s[i]=' ' then
begin
j:=i;
break;
end;
val(copy(s,2,j-2),t1);
delete(s,1,j);
if copy(s,1,5)='input' then dy1;
if copy(s,1,3)='sum' then dy2;
if copy(s,1,3)='avg' then dy3;
if copy(s,1,6)='output' then dy4;
end;
end.
相关链接
貌似并没有呢