Codeforces Round #637 Div. 2
比赛链接 https://codeforces.com/contest/1341
比赛记录 https://blog.csdn.net/cheng__yu_/article/details/105395197
A. Nastya and Rice
题意:问两个区间有没有交集。
思路:询问有没有交集,直接用没有交集来判断比较好。
[
x
1
,
y
1
]
[x1,y1]
[x1,y1]、
[
x
2
,
y
3
]
[x2,y3]
[x2,y3] 当
y
1
<
x
2
y1<x2
y1<x2 或者
x
1
>
y
3
x1>y3
x1>y3 的时候没有交集
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5,mod=1e9+7;
const int inf=0x3f3f3f3f;
ll t,n,a,b,c,d;
int main()
{
cin>>t;
while(t--)
{
cin>>n>>a>>b>>c>>d;
ll x1=a-b,y1=a+b;
ll x2=c-d,y2=c+d;
ll minn=x1*n,maxx=y1*n;
if(maxx<x2||minn>y2)
puts("No");
else
puts("Yes");
}
return 0;
}
B. Nastya and Door
题意:求峰点数量最多,且最左边的起点。
思路:只需要枚举每个起点,取个峰点最多,起点最靠左的就好了
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5,mod=1e9+7;
const int inf=0x3f3f3f3f;
int t,n,k;
int a[maxn],b[maxn],c[maxn];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]),b[i]=0;
for(int i=2;i<=n-1;++i)
if(a[i]>a[i-1]&&a[i]>a[i+1])
b[i]=1;
for(int i=1;i<=n;++i)
b[i]+=b[i-1];
int ans=0,index=1;
for(int i=1;i+k-1<=n;++i)
{
int e=i+k-1;
int sum=b[e-1]-b[i];
if(ans<sum)
{
ans=sum;
index=i;
}
}
printf("%d %d\n",ans+1,index);
}
return 0;
}
贴一份比赛时乱七八糟、分类讨论的代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5,mod=1e9+7;
const int inf=0x3f3f3f3f;
int t,n,k;
int a[maxn],b[maxn],c[maxn];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]),b[i]=0,c[i]=0;
for(int i=2;i<=n-1;++i)
{
if(a[i]>a[i-1]&&a[i]>a[i+1])
b[i]=1;
}
for(int i=1;i<=n;++i)
c[i]=c[i-1]+b[i];
int ans=0,index=-1;
for(int i=2;i<=n-1;++i)
{
if(b[i]==1)
{
int e=min(n-1,i+k-3);
int sum=c[e]-c[i-1];
if(ans<sum)
{
ans=sum;
int ss=i-2,tt;
for(int j=e;j>=i;--j)
{
if(b[j]==1)
{
tt=i-1+k-1-(j+1);
break;
}
}
for(int j=i-1;j>=2;--j)
{
if(b[j]==1)
{
ss=i-1-(j+1);
break;
}
}
if(ss>=tt)
{
index=i-1-tt;
}
else
{
index=i-1-ss;
}
}
}
}
index=max(1,index);
printf("%d %d\n",ans+1,index);
}
return 0;
}
C. Nastya and Strange Generator
题意:问给定的序列能不能用题中所给的算法生成。
比如,题目中举的例子:原序列a: [ 2 3 * * 1 ],先得出 r 数组 [ 3, 3 ,3 ,4 , * ] 。r 数组的意思是:原序列当前位置右边第一个空格的位置,比如原序列 a 索引为1的位置的右边第一个空位是 3。索引为2的位置的右边第一个空位是 3。索引为4的位置的右边第一个空位是它自己 4 。索引为5的位置的右边没有空位 标记为 *
然后得出count数组,对 r 数组中的出现的数字计数 为 [ 0 , 0 , 3 , 1 ,0 ]。3代表3在
r
r
r 里面出现了3次。然后count数组中最大的数字出现的位置,就是原序列中选择的位置。这里count数组中索引为3的位置数字最大为3,所以原序列选择第3个位置填充数字。
思路:假设从 1到n 构造一个序列,在放 i 的时候,如果 i - 1 的右边还有空位,就必须放在 i -1 后面。如果 i -1 右边一位没有空位了,就可以任意放。比如,[* * 2 * 1 ],放3的时候,就只能是 [* * 2 3 1] 。然后
4
4
4 就可以随便放,因为3的右边一位已经没有空位了。
所以就遍历一遍看每个数字的位置是否合法。从 1 到 n,检查数字 i 是否合理的时候,这个数字 i 的右边一位,要么是 i +1 ,要么就是被 小于 i 的数字填过了。
实现
记录下每个数字的位置, i 从1 到 n 判定位置是否合法
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5,mod=1e9+7;
const int inf=0x3f3f3f3f;
int t,n,a[maxn],pos[maxn],visit[maxn];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
visit[i]=0;
pos[a[i]]=i;
}
visit[n+1]=1;
bool valid=true;
for(int i=1;i<=n;++i)
{
int p=pos[i];
visit[p]=1;
if(i+1==a[p+1]||visit[p+1])
continue;
valid=false;
break;
}
if(valid)
puts("Yes");
else
puts("No");
}
return 0;
}
D. Nastya and Scoreboard
题意:有
n
n
n 块7位长的电子数字牌(中间缺了一些),给定
k
k
k 根木棍做补充,问能够组成的最大数字是多少。
思路:
- 可以用dfs(i,cnt)表示当前为凑第 i 个数字,已经用了cnt根木棍。每次都贪心的取最大,第一次 i==n+1,cnt==k的时候,就是最终的答案。
- dp[i][j]表示从数字牌 i 到 n 用 j 根木棍的方案的集合(只要有一种方案),属性是能否凑成。
转移: 先判断 第 i 个 牌子 凑成 [0,9]中任意一个数组需要的木棍数cnt。当前需要cnt根,那么 它就是从 j - cnt 转移过来的。
d p [ i ] [ j ] ∣ = d p [ i ] [ j − c n t ] j ∈ [ c n t , k ] dp[i][j] \ |=dp[i][j-cnt] \ j\in[cnt,k] dp[i][j] ∣=dp[i][j−cnt] j∈[cnt,k]
最后,判断dp[1][k],如果为true就贪心构造。需要 c n t cnt cnt 根并判断 d p [ i + 1 ] [ k − c n t ] dp[i+1][k-cnt] dp[i+1][k−cnt] 是否为真
总结
- 这道题最巧妙的地方是能够看出:完好的数字牌和破损的数字牌之间,可以用二进制之间的关系来作补充
- 判断 破损数字牌能否变成【1,9】的数字牌: ( v a l [ i ] & t a b [ j ] ) = = v a l [ i ] (val[i]\&tab[j])==val[i] (val[i]&tab[j])==val[i]
- 判断 能够变成 需要的木棍数:__builtin_popcount((tab[j]^val[i]))
- 最后构造最大数的时候,是判断取了这一位之后,后一位是否为真。即dp[i+1][k-cnt]是否为真
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000+5,maxm=1e5+5;
const int mod=1e9+7,inf=0x7f7f7f7f;
int n,k;
string numbers[]={"1110111", "0010010", "1011101", "1011011", "0111010", "1101011", "1101111", "1010010", "1111111", "1111011"};
int tab[10];
int val[maxn];
bool dp[maxn][maxn];
int calc(string s)
{
int res=0;
for(int i=0;i<7;++i)
if(s[i]=='1')
res|=(1<<7-i-1);
return res;
}
int main()
{
for(int i=0;i<=9;++i)
tab[i]=calc(numbers[i]);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)
{
string s;
cin>>s;
val[i]=calc(s);
}
dp[n+1][0]=true;
for(int i=n;i>=1;--i)
{
for(int j=0;j<=9;++j)
{
if((val[i]&tab[j])==val[i])
{
int cnt=__builtin_popcount((val[i]^tab[j]));
for(int x=cnt;x<=k;++x)
dp[i][x]=dp[i][x]|dp[i+1][x-cnt];
}
}
}
if(!dp[1][k])
puts("-1");
else
{
string ans="";
for(int i=1;i<=n;++i)
{
for(int j=9;j>=0;--j)
{
if((val[i]&tab[j])==val[i])
{
int cnt=__builtin_popcount(val[i]^tab[j]);
if(dp[i+1][k-cnt])
{
k-=cnt;
ans+=j+'0';
break;
}
}
}
}
cout<<ans<<"\n";
}
return 0;
}
DFS
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000+5,mod=1e9+7;
const int inf=0x3f3f3f3f;
int n,k;
int val[10],now[maxn];
bool visit[maxn][maxn];
int ans[maxn];
string numbers[]={"1110111", "0010010", "1011101", "1011011", "0111010", "1101011", "1101111", "1010010", "1111111", "1111011"};
int f(string s)
{
int res=0;
int n=s.size();
for(int i=0;i<n;++i)
{
if(s[i]=='1')
res|=(1<<n-1-i);
}
return res;
}
void dfs(int i,int cnt)
{
if(cnt>k||visit[i][cnt])
return;
visit[i][cnt]=true;
if(i==n+1)
{
if(k==cnt)
{
for(int i=1;i<=n;++i)
printf("%d",ans[i]);
exit(0);
}
return;
}
for(int j=9;j>=0;--j)
{
ans[i]=j;
if((val[j]&now[i])==now[i])
dfs(i+1,cnt+__builtin_popcount(val[j]^now[i]));
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=0;i<=9;++i)
val[i]=f(numbers[i]);
for(int i=1;i<=n;++i)
{
string s;
cin>>s;
now[i]=f(s);
}
dfs(1,0);
puts("-1");
return 0;
}
E. Nastya and Unexpected Guest(01BFS)
题意:在一条长度为 n的线段上,一个人要从0 到达n。线段[0,n]上有很多的安全岛。一个人只能在绿灯的时候才可以移动而且绿灯时必须在移动,红灯时必须待在岛上。问这个人从0到n最短需要的时间。
思路:用dis[i][j]表示当前在第i个岛,绿灯时间为
j
j
j 经历过的红绿灯轮次。0/1BFS的时候,记录两个量 pos和sec,pos表示当前是第几个岛,sec表示经过的绿灯秒数。那么在移动时,当sec<g时,权值为0,sec==g时权值为1,sec>g时,说明无法到达当前岛屿直接舍弃
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+5,mod=1e9+7;
const int inf=0x3f3f3f3f;
int n,m,g,r;
int d[maxn];
bool visit[10010][1010];
int times[10010][1010];
struct node
{
int pos,sec;
};
int bfs()
{
deque<node> q;
q.push_back({1,0});
visit[1][0]=true;
int ans=inf;
while(!q.empty())
{
node t=q.front();
q.pop_front();
if(t.sec==0)
{
if(g>=d[m]-d[t.pos])
{
ans=min(ans,d[m]-d[t.pos]+times[t.pos][t.sec]*(g+r));
}
}
if(t.pos>1)
{
int pos=t.pos-1;
int tt=d[t.pos]-d[pos]+t.sec;
if(tt<g&&!visit[pos][tt])
{
visit[pos][tt]=true;
q.push_front({pos,tt});
times[pos][tt]=times[t.pos][t.sec];
}
else if(tt==g&&!visit[pos][0])
{
visit[pos][0]=true;
q.push_back({pos,0});
times[pos][0]=times[t.pos][t.sec]+1;
}
}
if(t.pos<m)
{
int pos=t.pos+1;
int tt=d[pos]-d[t.pos]+t.sec;
if(tt<g&&!visit[pos][tt])
{
visit[pos][tt]=true;
q.push_front({pos,tt});
times[pos][tt]=times[t.pos][t.sec];
}
else if(tt==g&&!visit[pos][0])
{
visit[pos][0]=true;
q.push_back({pos,0});
times[pos][0]=times[t.pos][t.sec]+1;
}
}
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
scanf("%d",&d[i]);
sort(d+1,d+1+m);
scanf("%d%d",&g,&r);
int ans=bfs();
if(ans==inf)
ans=-1;
printf("%d\n",ans);
return 0;
}