2019.01.26【NOIP提高组】模拟 B 组 比赛总结
题目
T1 天平
Description
FJ有一架用来称牛的体重的天平。与之配套的是N(1<=N<=40)个已知质量的砝码(所有砝码质量的数值都在31位二进制内)。每次称牛时,他都把某头奶牛安置在天平的某一边,然后往天平另一边加砝码,直到天平平衡,于是此时砝码的总质量就是牛的质量(FJ不能把砝码放到奶牛的那边,因为奶牛不喜欢称体重,每当FJ把砝码放到她的蹄子底下,她就会尝试把砝码踢到FJ脸上)。天平能承受的物体的质量不是无限的,当天平某一边物体的质量大于C(1<=C<2^30)时,天平就会被损坏。
砝码按照它们质量的大小被排成一行。并且,这一行中从第3个砝码开始,每个砝码的质量至少等于前面两个砝码(也就是质量比它小的砝码中质量最大的两个)的质量的和。
FJ想知道,用他所拥有的这些砝码以及这架天平,能称出的质量最大是多少。由于天平的最大承重能力为C,他不能把所有砝码都放到天平上。
现在FJ告诉你每个砝码的质量,以及天平能承受的最大质量。你的任务是选出一些砝码,使它们的质量和在不压坏天平的前提下是所有组合中最大的。
Input
第1行: 两个用空格隔开的正整数,N和C。
第2…N+1行: 每一行仅包含一个正整数,即某个砝码的质量。保证这些砝码的质量是一个不下降序列。
Output
第1行: 一个正整数,表示用所给的砝码能称出的不压坏天平的最大质量。
Sample Input
3 15
1
10
20
Sample Output
11
Hint
【样例说明】
FJ有3个砝码,质量分别为1,10,20个单位。他的天平最多只能承受质量为15个单位的物体。用质量为1和10的两个砝码可以称出质量为11的牛。这3个砝码所能组成的其他的质量不是比11小就是会压坏天平。
T2 游历的路线
Description
我们的郭嘉大大经过一段时间发现了袁绍这个人干大事而惜身,见小利而忘义,又逢曹操在招兵买马,决定逃离袁绍去投曹操,而我们的曹操在第M天招募良材,我们的郭嘉大大既不能早去,也不能晚去,于是乎,他就趁着这一段时间到其他的城市游历一番,而每两个城市之间只能坐马车来往,由于我们的郭嘉大大很贪钱,他想用最少的费用,所以需要我们帮他求出这一个最小的费用。
Input
第一行包含两个数n,m, 表示有n个城市,和m天后曹操招纳良材。城市一就是郭嘉所在的城市,城市n就是曹操处。接下来n * (n – 1)行描述马车乘坐表。 第2到第n行就是描述的城市1到2… n的马车乘坐表. 第n + 1到第2n-1行描述的城市2到城市1,3…n的马车乘坐表… … 对每一行,首先有一个数T,表示城市I到城市J的马车以T为周期,接下来有T个数,表示每天的马车的价格,如果价格为0则表示没有马车可坐。(n <= 100, m <= 200, T <= 20, Price <= 50000)
Output
如果存在这样的路线使郭嘉第m天到达曹操处,则输出最少的费用,否则输出0!
Sample Input
3 5
2 130 150
3 75 0 80
2 110 100
4 60 70 60 50
3 0 135 140
2 70 80
Sample Output
355
T3 食物
Description
Input
Output
Sample Input
4
1 1 7
14 2 1
1 2 2
1 1 10
10 10 1
5 7 2
5 3 34
1 4 1
9 4 2
5 3 3
1 3 3
5 3 2
3 4 5
6 7 5
5 3 8
1 1 1
1 2 1
1 1 1
Sample Output
4
14
12
TAT
Data Constraint
总结
这次比赛比昨天可做了一些。
T1:
死磕了2h,毫无头绪,感觉像是贪心,又像是二分。最后只好打暴力了。
T2:
一眼出正解,此乃背包!设表示第 j 天在城市 i 的最少花费。然后就一通乱搞……
但坑×的是,郭嘉居然不能在城市里连续呆几天!
T3:
感觉不可做。应该是DP吧!
OJ bug报告:
11:17:26
什么鬼?我半个小时前交的代码居然……
11:18:46
啊,OJ爆了!
11:19:21
好吧,OJ真的爆了。我只好默默地拿出作业……
11:20:46
还没运行完啊!
这时,XC怒不可歇地冲了进来说:“(代码)没运行完的不要重复交啊,我看有的人都交了好几遍了。你们这不是给OJ添堵吗?”
N个人愧怍地低下了头。
After the contest
于是乎,成绩刚出的时候
我居然没有爆〇耶!一定是昨晚做值日攒足了人品(吃宵夜被抓T_T,结果被XC安排去扫3、4楼所有教室,别的还好,在肮脏的403扫了足足7袋垃圾!)。这个故事告诉我们:要多做值日,实在不行可以在表上多登几个名字。做值日不要紧,人品才是最重要的<(▰˘◡˘▰)
睡醒午觉后,就多了100分了。
题解
T1
解法1(水法)
这题我打暴力拿了50分~按理说我接下来会废掉暴力重新打正解的,可是我发现那50的暴力并没有TLE耶!
于是我就开始查错误……提交……AC!
虽然暴力理论上的时间复杂度是 ,但是加上一堆猥琐剪枝后它完全可以变成这样:
Amazing!
解法2
大佬们用了折半搜索。
先是枚举前半段,答案放入数组a中,再是枚举后半段,答案放入数组b中。这时我们就得到了两个长度为2^(n/2)的数组,只要想办法把它们组合起来,使结果合法并最大化就可以了。
于是自然而然地就想到了二分。可以先把数组a排序,再在数组b中枚举每一个数,在a数组中二分查找满足 的最大a值,更新答案即可。
解法3
LZY说不一定要二分查找。
可以把两个数组都排序,接着一个指针指着 ,另一个指着的 ,用类似于双指针搜索的方法查找就可以了。
当然,这个方法的常数似乎不太优秀……
T2
解法一
比赛时我一眼出正解,这题难道不是DP吗?
设表示第 j 天在城市 i 的最少花费。状态转移方程就为
表示第 j 天从 k 到 i 的费用。
结果发现连样例都过不了。后来去掉了第一行的转移就AC了。
坑啊!为什么郭嘉大大不能在同一个城市住上几天呢?
解法二
这题也可以用最短路做,其实就是在原来最短路的基础上多开一维存时间。
T3
完全背包问题。
设表示美味值等于 i 时的最小体积,表示运费为 i 时的最大体积。
乱搞一下就可以了。
感悟
1 ) RP很重要,平时生活中要多点积累RP。当然我们也要发扬乐于助人的精神,积极帮助他人攒人品,因此要多让别人请吃饭,也要促使他人搞卫生(不讲题目大意的时候千万不要提醒)
2 ) 我的基本功不是很扎实,一些基础的算法如搜索掌握程度不佳,要加强。3 ) 雷灏讲题时一定要安静,最好忘记时间。4 ) 总结写那么长会很耗时间的。雷灏是怎么做到的???
开源盛世
T1
#include<cstdio>
using namespace std;
#define ll long long
#define N 50
int a[N],n,m,ans;
ll f[N];
void dfs(int k,ll sum)
{
if(sum>m) return;
if(sum>ans) ans=sum;
if(!k) return;
if(sum+f[k]<=m)
{
if(sum+f[k]>ans) ans=sum+f[k];
return;
}
dfs(k-1,sum);
if(sum+a[k]<=m) dfs(k-1,sum+a[k]);
}
int main()
{
int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++) scanf("%d",&a[i]),f[i]=f[i-1]+a[i];
dfs(n,0);printf("%d\n",ans);
return 0;
}
T2
#include<cstdio>
using namespace std;
#define inf 999999999
#define N 105
#define M 205
int f[N][M],a[N][N][25];
inline int min(int x,int y){return x<y?x:y;}
int main()
{
freopen("lines.in","r",stdin);
freopen("lines.out","w",stdout);
int n,m,i,j,k,t;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
if(j!=i)
{
scanf("%d",&a[i][j][0]);
for(k=1;k<=a[i][j][0];k++)
scanf("%d",&a[i][j][k]);
}
for(j=0;j<=m;j++) f[i][j]=inf;
}
f[1][0]=0;
for(j=1;j<=m;j++)
{
for(i=1;i<=n;i++)
{
//f[i][j]=f[i][j-1];
for(k=1;k<=n;k++) if(a[k][i][0])
{
t=a[k][i][j%a[k][i][0]?j%a[k][i][0]:a[k][i][0]];
if(t)
f[i][j]=min(f[i][j],f[k][j-1]+t);
}
}
}
if(f[n][m]!=inf) printf("%d\n",f[n][m]);
else puts("0");
return 0;
}
T3
#include<cstdio>
using namespace std;
#define inf 999999999
#define P 50105
#define N 5000
int n,m,p,num1,num2,f[P],g[P],t[N],u[N],x[N],y[N];
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;}
int main()
{
int test,i,j,k,a,b,c,ans;
scanf("%d",&test);
while(test--)
{
scanf("%d%d%d",&n,&m,&p);
num1=num2=0,ans=inf;
for(i=1;i<=n;i++)
{
scanf("%d%d%d",&a,&b,&c);
for(k=1;k<=c;k<<=1)
{
t[++num1]=a*k;
u[num1]=b*k;
c-=k;
}
if(c) t[++num1]=a*c,u[num1]=b*c;
}
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&c);
for(k=1;k<=c;k<<=1)
{
x[++num2]=a*k;
y[num2]=b*k;
c-=k;
}
if(c) x[++num2]=a*c,y[num2]=b*c;
}
for(i=1;i<P;i++) f[i]=inf,g[i]=-inf;
for(i=1;i<=num1;i++)
for(j=p+100;j>=0;j--)
if(j>=t[i])
f[j]=min(f[j],f[j-t[i]]+u[i]);
for(j=p+99;j>=p;j--) f[j]=min(f[j+1],f[j]);
ans=50000;
for(i=1;i<=num2;i++)
for(j=ans;j>=0;j--)
{
if(j>=y[i])
g[j]=max(g[j],g[j-y[i]]+x[i]);
if(g[j]>=f[p]) ans=min(ans,j);
}
if(ans<50000) printf("%d\n",ans);
else puts("TAT");
}
return 0;
}