你手上有 w 元钱,并且你已经预测出了近 n 天的某股票行情(这个股票每股价格),你只能选择一天买入股票,问你最多能在这 n 天中赚多少钱。
显然股票肯定也是同一天卖出的(在第 i 天买入的话,肯定选择某个第 j 天卖出, j>i且第j 天的股票价格是在 [i+1,n] 天中是最大的),因此打暴力就行了,本场比赛最水的题。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 1000
using namespace std;
int p[MAXN],n,w;
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&w);
for(int i=1;i<=n;i++) scanf("%d",&p[i]);
int ans=0;
for(int i=1;i<=n;i++)
{
int num=w/p[i]; //第i天买入num份股票
for(int j=i+1;j<=n;j++)
ans=max(ans,num*p[j]-num*p[i]);
}
printf("%d\n",ans);
}
return 0;
}
求 ∑ni=1[ni],n<=1012
如果直接for循环一遍的话是 O(n) 的,显然TLE
但是发现向下取整的很有趣的性质,即 [nx] 在连续的一段 i 答案是相同的,并且 在1<=x<=n 的情况下, [nx] 最多有 n√ 种不同的值,这是很显然的。
因此我们可以枚举 [ni] 的取值,并能维护两个指针 i,j 表示当前的 [nx] 的取值是对应于 x∈[i,j] 的,这样复杂度就降到了 O(n√) 。
这种优化方法即分块优化思想(不是区间查询问题里的那个分块),在很多类似问题里都很常见。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MOD 1000000
using namespace std;
typedef long long int LL;
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
LL n,ans=0;
scanf("%I64d",&n);
LL i=1;
while(i<=n)
{
LL j=n/(n/i);
ans=(ans+((j-i+1)*(n/i))%MOD)%MOD;
i=j+1;
}
printf("%I64d\n",ans);
}
return 0;
}
你有
1000 、 2000 、 3000 、 5000 、 1000∗101 、 2000∗101 、 3000∗101 、 5000∗101 …… 5000∗10c 的钞票,每种面值的钞票若干张,然后要用这样纸币凑齐 W 元钱,求最少要多少张纸币,以及最小方案的方案数。
可以想到一个非常显然的思路:
首先把 W 除以1000(越南猴子的钞票通货膨胀很严重啊233),这样钞票就变成了1、2、3、5、10、20、30、50……这样的面值比较好看,然后把现在的 W 按照位数分解,对于个位到第 c 位,每一位均是一个个位数,对这 c 个个位数求出每一位需要凑齐的纸币最少要多少张,我们可以打表得到 need[i]=数位i(i<10) 需要最少多少张纸币才能凑齐, ways[i]=数位i 用纸币凑齐的最小方案有多少种,然后对于 c+1 到最高位,只能用最大的面值( 5∗10c )来凑,用上述方法求和即为最小方案,乘法计数即可得到最小方案的个数。这样贪心很显然是可以得到最少要多少张纸币的,但是无法正确地求出最小方案的种数,比如下面这个反例: W=110 ,用上面的贪心可以得到最小方案有一种: 50+50+10 ,但是实际上还有一种最小方案: 50+30+30 。
究其原因就是因为我们僵化了想法, W 太大的部分不一定非得要用 5∗10c 凑齐,可以留一个 5∗10c ,即留下 W mod 5∗10c+5∗10c 。
因此我们先用 [(W−5∗10c)5∗10c] 张 5∗10c 面值的纸币,对剩下的 W′=W−[(W−5∗10c)5∗10c]∗(5∗10c) 元,再用上面的贪心做法做一遍即可。
对数字 N 进行 K 次变换,每次变换如下:
N=N+N mod 100
求经过 K 次变换后 N 最终变成什么数字
如果 O(K) 打暴力的话,单组数据在2s的时限内玩玩常数是有可能AC的,但是由于题目最多500组数据,因此还是会TLE。
最终的答案实际上可以看成是 XK=XK−1+AK ,其中 Xt 是做了 t 次变换后得到的答案。 At= 在做第 t 次变换时给答案增加上去的那个值。
At 可以看成是 (N+...(N+(Nmod100))...mod100) 这样的形式,因为一直是 mod 100 ,因此可以改写成 At=(2t−1N) mod 100 。也可以写成递推形式, At=2At−1 mod 100,A1=N 。
可以发现,在 t>2 时, At=(2t−3N) mod 25=At−2 ,于是出现了循环节,当然也可以通过打表找循环规律。那么我们没有必要挨个挨个加 K 次答案,只需要把循环节的那部分乘上循环节的出现次数就可以了,剩下的不是循环节的部分也不是很长,因此这样做可以AC掉。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 1000
using namespace std;
typedef long long int LL;
LL N,K;
LL hash[110];
LL step[110]; //step[i]= X mod 100=i时移动的步长
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(hash,-1,sizeof(hash));
memset(step,0,sizeof(step));
scanf("%lld%lld",&N,&K);
LL X=N%100;
LL i;
LL addv=0;
for(i=1;i<=K;i++)
{
addv+=X;
if(hash[X]!=-1)
{
addv+=((K-i)/(i-step[X]))*(addv-hash[X]);
i+=(i-step[X])*((K-i)/(i-step[X]));
X=(X*2)%100;
break;
}
else
{
hash[X]=addv;
step[X]=i;
X=(X*2)%100;
}
}
for(;i<K;i++)
{
addv+=X;
X=(X*2)%100;
}
printf("%lld\n",N+addv);
}
return 0;
}
给定一个 N∗M 大小的棋盘,棋盘上的点 (i,j),(0<=i<=N,0<=j<=M) 上的数字为 i∗M+j ,要从中找一个面积最小的子矩形,使得该子矩形的元素之和为 L ,求该子矩形的最小面积,若无解输出-1
不妨设这个子矩形最左边的列号是 x1 ,最右边的列号是 x2 ,最上边的行号是 y1 ,最下边的行号是 y2 。很容易推出这个子矩形的元素之和S与其关系为:
2S=(x2−x1+1)(y2−y1+1)[x1+x2+M(y1+y2)]
设 X=(x2−x1+1) , Y=(y2−y1+1) ,显然 XY 就是最终答案
再把上式改写
2S=XY[x1+x2+M(y1+y2)]
那么我们最终可以爆枚 2L 的约数 X和Y ,然后判断爆枚出的 XY 是否合法,并维护最小的 XY 值,由于 L<=1012 ,而且一个数的约数个数非常少,爆枚复杂度为 O(2L的约数个数) ,因此可以通过此题。
给一个 N∗M 大小的棋盘,棋盘上某些格子有障碍物。一个火星车要空降到棋盘的某一点,然后从这一点开始,每次从下列两种方法循环依次选择一种方法来移动:
1、向右拐弯,前进一格
2、向左拐弯,前进一格
这个火星车不能越过棋盘的边界,也不能经过某些障碍格子,问这个火星车最多能遍历多少格子。
可以发现这个火星车从降落点开始的行走路线如下图一样
这个路线是放射状的,并且开始的方向一定是像下图这样:
可以将这四条路线分别标记为从左上、左下、右上、右下出发而来的路线,由于这四条路线的形态是固定的,因此我们可以对每条路线,从路线的终点倒过来dp, f[i][j]= 某条路线走到 (i,j) ,沿途遍历过的格子个数。然后注意一定要在路线上每隔一个格子转移一次状态,即如下面的伪代码一样(伪代码来自本次比赛的tutorial):
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 1100
using namespace std;
char s[MAXN];
int map[MAXN][MAXN];
int fzs[MAXN][MAXN],fzx[MAXN][MAXN],fys[MAXN][MAXN],fyx[MAXN][MAXN]; //左上、左下、右上、右下为起点到(i,j)的方案数
int n,m;
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(fzs,0,sizeof(fzs));
memset(fzx,0,sizeof(fzx));
memset(fys,0,sizeof(fys));
memset(fyx,0,sizeof(fyx));
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
{
scanf("%s",s+1);
for(int j=1; j<=m; j++)
map[i][j]=s[j]=='1';
}
//左上角开始的路线的DP
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
if(!map[i][j])
fzs[i][j]=0;
else if(!map[i][j-1]) fzs[i][j]=1;
else fzs[i][j]=fzs[i-1][j-1]+2;
//左下角路线开始的DP
for(int i=n; i>=1; i--)
for(int j=1; j<=m; j++)
if(!map[i][j])
fzx[i][j]=0;
else if(!map[i+1][j]) fzx[i][j]=1;
else fzx[i][j]=fzx[i+1][j-1]+2;
//右上角路线开始的DP
for(int i=1; i<=n; i++)
for(int j=m; j>=1; j--)
if(!map[i][j])
fys[i][j]=0;
else if(!map[i-1][j]) fys[i][j]=1;
else fys[i][j]=fys[i-1][j+1]+2;
//右下角路线开始的DP
for(int i=n; i>=1; i--)
for(int j=m; j>=1; j--)
if(!map[i][j])
fyx[i][j]=0;
else if(!map[i][j+1]) fyx[i][j]=1;
else fyx[i][j]=fyx[i+1][j+1]+2;
int maxans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
maxans=max(maxans,fzx[i][j]+fys[i][j]+fzs[i][j]+fyx[i][j]-3);
printf("%d\n",maxans);
}
return 0;
}