题目传送门:
A. Prison Break
有一个n*m的监狱,每一个格子有一个犯人,坐标为( r , c )的格子有一条逃生通道,犯人每一秒可以向相邻的一个格子移动,问所有犯人到达逃生通道最久需要多少秒。
判断监狱四个角上的犯人到达逃生通道的时间即可
#include
using namespace std;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m,r,c;
scanf("%d%d%d%d",&n,&m,&r,&c);
int res=0;
res=max(res,abs(1-c)+abs(1-r));
res=max(res,abs(n-r)+abs(m-c));
res=max(res,abs(1-r)+abs(m-c));
res=max(res,abs(n-r)+abs(1-c));
printf("%d\n",res);
}
//system("pause");
return 0;
}
题目传送门:
B. Repainting Street
有n个房子,每个房子有自己初始颜色ci。画家每次可以画连续的k个房子,可以画成任意颜色,或者不变。问最少画几次可以使所有的房子都变成一样的颜色。
我们首先看题目的数据范围,初始颜色最多只有100种,房屋最多有1e5。所以我们枚举每种颜色,进行操作然后取小即可。
#include
using namespace std;
const int N=1e5+10;
int c[N],ans[105];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,k;
scanf("%d%d",&n,&k);
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);
ans[c[i]]=1;
}
int maxn=0x3f3f3f3f;
for(int i=1;i<=100;i++)
{
if(ans[i]==0) continue;
int p=0,num=0;
int idx=1;
while(idx<=n)
{
num=0;
while(c[idx]!=i&&idx<=n)
{
num++;
idx++;
}
if(num>0)
{
p+=num/k;
if(num%k)
{
p++;
idx=idx+k-num%k;
}
}
while(c[idx]==i&&idx<=n) idx++;
}
maxn=min(maxn,p);
}
printf("%d\n",maxn);
}
//system("pause");
return 0;
}
题目传送门:
C. Bouncing Ball
有n个单元格,有一个小球会从左边来,第一次会落在单元格p,然后会落在单元格p+k,然后会落在p+2k……直到弹出第n个单元格为止。只有当当前位置的单元格有平台时,小球才能继续往下弹。你有两个操作:
1、花费x秒的时间在任意单元格添加一个平台。
2、花费y秒的时间删去最前面的单元格。
问最少需要多少秒,可以使小球弹出第n个单元格。
我们发现,如果从前往后枚举p点所在的位置,然后从该点开始一步一步往下跳进行模拟,那么肯定会超时。然后我们观察到很重要的一个特征是从第一次之后,接下去每次跳的距离都为k。最后一步的落点一定在n-k+1~n之间,那么从后往前推导即可,有点动态规划和后缀和的意思。
#include
using namespace std;
const int N=1e5+10;
typedef long long LL;
LL a[N],f[N*2];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,p,k;
scanf("%d%d%d",&n,&p,&k);
for(int i=1;i<=n;i++)
scanf("%1d",&a[i]);
int x,y;
scanf("%d%d",&x,&y);
memset(f,0,sizeof(f));
for(int i=n;i>=1;i--)
{
if(a[i]==0)
{
if(i+k<=n) f[i]=f[i+k]+1;
else f[i]=1;
}
else f[i]=f[i+k];
}
LL minn=0x3f3f3f3f;
for(int i=n;i>=1;i--)
{
if(i-p<0) break;
LL num=f[i]*x+(i-p)*y;
minn=min(minn,num);
}
printf("%lld\n",minn);
}
//system("pause");
return 0;
}
题目传送门:
D. XOR-gun
给你一个长度为n的单调不降序列a,每次操作可以取出相邻的两个数,异或后插入原来的位置,问最少操作几次才能使该数列不是单调不降序列。
我们从位运算的角度考虑,如果有三个连续的数的最高位相同,那么后面两个数的异或结果一定要比第一个数小。(比赛的时候咋没想到呢QAQ)。而数据范围是1e5<2^30。所以当n>60时,一定会存在连续的三个数的最高位相同,那么直接输出1即可。对于小于60的情况,我们可以暴力枚举合并的左区间和右区间,时间复杂度为60 ^ 3 ,是完全可行的。
#include
using namespace std;
const int N=1e5+10;
int a[N];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
a[i]=a[i-1]^x;
}
if(n>60) printf("1\n");
else
{
int maxn=1e9;
for(int l=1;l<n-1;l++)
{
for(int mid=l;mid<n;mid++)
{
for(int r=mid+1;r<=n;r++)
{
if((a[mid]^a[l-1])>(a[r]^a[mid]))
maxn=min(maxn,mid-l+r-mid-1);
}
}
}
if(maxn==1e9) printf("-1\n");
else printf("%d\n",maxn);
}
//system("pause");
return 0;
}
这道题让我学到了连续几个数的异或结果该怎么求。
当做到二进制异或的题目没有头绪的时候,还是应该多从二进制的一些特征入手。