Fixing Banners(DFS)
比赛链接:https://codeforces.com/gym/102394/problem/F
题目大意
现在有
6
6
6个字符串,你需要从每一个字符串中各取出一个字符,最后拼成"harbin"(顺序无所谓,只要最后得到的
6
6
6个字符是这
6
6
6个即可)。
问是否可以完成任务,可以输出"
Y
e
s
Yes
Yes",否则输出"
N
o
No
No"。
思路
第一想法就是DFS硬模拟,从第一个字符串开始搜,T掉了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string flag;
map<char,int> mp;
bool vis[10];
string s[10];
void init()
{
mp['h']=1;
mp['a']=2;
mp['r']=3;
mp['b']=4;
mp['i']=5;
mp['n']=6;
}
void dfs(int deep)
{
if(deep==6){
flag="Yes";
return;
}
for(int i=0;i<s[deep].size();i++)
{
if(mp[s[deep][i]]>=1&&mp[s[deep][i]]<=6&&!vis[mp[s[deep][i]]])
{
vis[mp[s[deep][i]]]=true;
dfs(deep+1);
vis[mp[s[deep][i]]]=false;
}
}
}
int main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
init();
while(t--)
{
for(int i=0;i<6;i++)
cin>>s[i];
flag="No";
memset(vis,false,sizeof(vis));
dfs(0);
cout<<flag<<endl;
}
}
效率低的原因是在dfs()
函数中,我每次都是遍历整个字符串。假设每个字符串中的字母都有效(即每个字符串都是完全由’h'、'a'、'r'、'b'、'i'、'n'
组成),那我这时间复杂度直接起飞。
所以需要优化。
我们看字符串hhhhhhhh
,假设我们选中了第一个h
之后进行搜索,结果搜索失败。那么在它之后的h
一定也都是失败的。
这里我们就可以用空间换时间,定义二维数组m[10][10]
:
m[i][j]=1:第i个字符串含有'harbin'中的第j个字符
m[i][j]=0:第i个字符串不含有'harbin'中的第j个字符
这样我们在dfs()
函数中就无需遍历字符串的全部,只需要遍历一个长度为
6
6
6的一维数组,时间复杂度大大减小。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string flag;
string s[10];
map<char,int> mp;
bool vis[10];
int m[10][10];
void init()
{
mp['h']=1;
mp['a']=2;
mp['r']=3;
mp['b']=4;
mp['i']=5;
mp['n']=6;
}
void dfs(int deep)
{
if(deep==6)
{
flag="Yes";
return;
}
for(int i=1; i<=6; i++)
{
if(m[deep][i]&&!vis[i])
{
vis[i]=true;
dfs(deep+1);
vis[i]=false;
}
}
}
int main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
init();
while(t--)
{
for(int i=0;i<6;i++)
for(int j=1;j<=6;j++)
m[i][j]=0;
for(int i=0; i<6; i++)
{
cin>>s[i];
for(int j=0; j<s[i].size(); j++)
if(mp[s[i][j]])
m[i][mp[s[i][j]]]=1;
}
flag="No";
for(int i=1; i<=6; i++)
vis[i]=false;
dfs(0);
cout<<flag<<endl;
}
}
Interesting Permutation (构造序列+思维)
比赛链接:https://codeforces.com/gym/102394/problem/I
题目大意
数组 a a a是 1 1 1 ~ n n n的一种排列组合,它与数组 h h h的关系如下:
- l e n g t h ( a ) = l e n g t h ( h ) = n length(a)=length(h)=n length(a)=length(h)=n;
- h i = m a x ( a 1 , a 2 , . . . , a i ) − m i n ( a 1 , a 2 , . . . , a i ) ; h_i=max(a_1,a_2,...,a_i)-min(a_1,a_2,...,a_i); hi=max(a1,a2,...,ai)−min(a1,a2,...,ai);
现给出数组
h
h
h,请求出有多少个不同的数组
a
a
a符合条件。
答案对1e9+7
取模。
思路
构造题
讲究的就是一个思路清晰,阿巴博主在看完第一遍题目之后就已经大脑空白了。
不过这点难度博主还是可以接受的,构造题
再怎么说也比什么"后缀数组+RMQ+主席树"
让人看着舒服一些。
我们先根据a数组的特点,推断出正常的
h
h
h数组应该满足什么条件:
- h [ 1 ] = 0 h[1]=0 h[1]=0;
- 数组 h h h单调不递减;
- m a x ( h 1 , h 2 , . . . , h n ) = n − 1 max(h_1,h_2,...,h_n)=n-1 max(h1,h2,...,hn)=n−1;
排除以上三点之后,就需要想办法求出数组 a a a的方案数了。
-
定义
ans
为当前的方案数,初始值为 1 1 1; -
定义
cnt
为当前可用的中间数的数量,中间数的意思就是当前的最大数与最小数之间数字的个数。当n=1
时, h 1 = 0 h_1=0 h1=0,此时最大值与最小值相同,所以初始值为 0 0 0; -
如果
h[i]==h[i-1]
,说明a[i]
是处于a[1]~a[i-1]
中最大数与最小数之间的一个数字,那么方案数就变成ans*cnt
。又由于我们需要借用cnt
个中间数中的一个,所以cnt--
; -
如果
h[i]>h[i-1]
,则说明a[i]
是a[1]~a[i]
之间的最大数/最小数,此时a[i]
为最大值是ans
个方案,a[i]
为最小值也是ans
个方案,所以方案数变为2*ans
。 -
- 如果
a[i]
为最大值,设a[1]~a[i-1]
的最大数为mx
,则中间数会多出a[i]-mx-1
个,而这个a[i]-mx-1
其实就是h[i]-h[i-1]-1
:
- 如果
-
- 同理,当
a[i]
为最小值时,中间数仍然会多出h[i]-h[i-1]-1
个,所以cnt+=h[i]-h[i-1]-1
;
- 同理,当
所以说博主真的很不擅长构造题,真滴烦。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e5+100;
ll h[maxn];
int main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
{
int n,flag=1;
cin>>n;
h[0]=-1;
for(int i=1; i<=n; i++)
{
cin>>h[i];
if(h[i]<h[i-1]||h[i]>=n||h[1]!=0)
flag=0;
}
if(flag&&h[n]==n-1)
{
ll ans=1;
ll cnt=0;
for(int i=2; i<=n; i++)
{
if(h[i]>h[i-1])
{
ans=(ans*2)%mod;
cnt+=h[i]-h[i-1]-1;
}
else
{
ans=(ans*cnt)%mod;
cnt--;
}
}
cout<<ans<<endl;
}
else
cout<<"0"<<endl;
}
}
Justifying the Conjecture (思维)
比赛链接:https://codeforces.com/gym/102394/problem/J
题目大意
现给出一个正整数
n
n
n,请问
n
n
n是否可以被分成一个素数
x
x
x与一个合数
y
y
y,使得
x
+
y
=
n
x+y=n
x+y=n。
如果可以,请输入任意一组符合条件的
x
、
y
x、y
x、y;否则输出
−
1
-1
−1。
思路
众所周知(并不是):
- 素数之中只有 2 2 2是偶数,其他的素数都是奇数;
- 1 1 1既不是素数也不是合数;
这是一道特判题,而做特判题的技巧就是找最简单的方案,所以我们在这里就要从这个特殊的素数
2
2
2 下手。
先把一些特殊情况踢出掉:1,2,3,4,5
这五个数是无法拆分的,原因自己想。
那么除去这五个数之后,我们把剩下的数拆分为奇数与偶数。
- 如果n为奇数,即
x
+
y
x+y
x+y为奇数,此时只有一种可能:奇数+偶数=奇数。
我们取 x = 3 x=3 x=3,则 n − x n-x n−x就一定是一个不等于 2 2 2的偶数,那么 n − x n-x n−x就一定是一个合数; - 如果n为偶数,即
x
+
y
x+y
x+y为偶数,此时有两种可能:奇数+奇数=偶数 / 偶数+偶数=偶数。
如果取 y y y为奇数,我们还需要额外判断 y y y是否为合数;
如果取 y y y为偶数,只要 y ! = 2 y!=2 y!=2, y y y就一定是合数;
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
if(n<=5) cout<<"-1"<<endl;
else if(n&1)
cout<<"3"<<" "<<n-3<<endl;
else
cout<<"2"<<" "<<n-2<<endl;
}
}
Keeping Rabbits(水题)
比赛链接:https://codeforces.com/gym/102394/problem/K
题目大意
现在DreamGrid养了n只兔子,第
i
i
i只兔子的初始体重为
w
i
w_i
wi。每只兔子每天会增重
w
i
∑
j
=
1
n
w
j
\frac{w_i}{\sum_{j=1}^{n}w_j}
∑j=1nwjwi。
请求出k天后每只兔子的重量。
思路
这都能卡住的吗少年?
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
double a[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,k;
scanf("%d%d",&n,&k);
double sum=0;
for(int i=1;i<=n;i++){
scanf("%lf",&a[i]);
sum+=a[i];
}
for(int i=1;i<=n;i++){
a[i]+=k*1.0*a[i]/sum;
}
for(int i=1;i<=n;i++)
{
if(i!=1) printf(" ");
printf("%.8f",a[i]);
}
printf("\n");
}
}
后话
感谢阅读,希望能对你产生一点用处。
以下台词取自《银魂》第104集——真选组动乱篇:
(每个人的心里都藏着一个死小孩。)