A:Shrinking
题意:规定一种对字符串的操作:设字符串长度为N,一次操作之后可以得到一个长度为N-1的字符串,新字符串的第i个字符等于旧字符串的第i个字符或第i+1个字符。给定一个长度为n的字符串,问最少进行几次操作可以得到一个只含有一种字符的字符串。
分析:假如我们要将字符串最终变为"aa...a",首先原字符串中必须得有字符a。设原字符串中字符a的位置从小到大依次为x1,x2,...,xk(位置的范围为0~n-1)。那么对于最终的字符串,若长度不小于x1,则位置0~x1-1的字符a应由原字符串位置x1处的字符a得到,即考虑0~x1这一段,至少需要x1次操作。同样考虑x1+1~x2这一段、...、xk+1~n-1(可以认为位置n处的字符也是a)这一段至少需要的操作数,取个最大值就是把原字符串最终变为"aa...a"所需要的最少操作数。可以枚举最终字符串包含的那个字符,也可以直接扫一遍处理。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=200;
int n,Last[maxn],f[maxn];
char ch[maxn];
int idx(char c)
{
return c-'a';
}
int main()
{
cin>>ch;
n=strlen(ch);
memset(Last,-1,sizeof(Last));
for (int i=0;i<n;i++)
{
int c=idx(ch[i]);
f[c]=max(f[c],i-Last[c]-1);
Last[c]=i;
}
for (int i=0;i<26;i++) f[i]=max(f[i],n-Last[i]-1);
int ans=n;
for (int i=0;i<26;i++) ans=min(ans,f[i]);
cout<<ans;
return 0;
}
B:Colorful Hats
题意:有n顶带颜色的帽子,告诉你n个事实,第i个事实是不算第i顶帽子,其余帽子总共有ai种颜色。问是否存在满足所有事实的n顶帽子。
分析:可以注意到以下事实
1.a1,a2,...,an中的最大数与最小数至多差1。
设n顶帽子总共有m种颜色,那么,若第i顶帽子的颜色是独一无二的,则ai=m-1,否则ai=m。
2.若a1=a2=...=an=n-1,则存在满足所有事实的n顶帽子。
此时所有帽子颜色互不相同。
3.若a1=a2=...=an<n-1,则每顶帽子颜色都不是独一无二的,此时存在满足所有事实的n顶帽子的充要条件为2*a1<=n。
显然。
4.若a1,...,an不全相等,设n顶帽子总共有m种颜色,并且有s顶帽子的颜色是独一无二的,显然有n>s。则存在满足所有事实的n顶帽子的充要条件为s<m且2*(m-s)<=n-s。
显然。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,a[maxn];
int m,s;
bool check()
{
for (int i=1;i<=n;i++)
if (a[i]!=n-1) return 0;
return 1;
}
int main()
{
cin>>n;
for (int i=1;i<=n;i++) scanf("%d",&a[i]),m=max(m,a[i]);
if (check())
{
cout<<"Yes";return 0;
}
for (int i=1;i<=n;i++)
if (a[i]<m)
{
if (a[i]==m-1) s++;
else
{
cout<<"No";return 0;
}
}
if (m<s||2*(m-s)>n-s||(n>s&&m-s<1)) cout<<"No";
else cout<<"Yes";
return 0;
}
C:+/- Rectangle
题意:问是否存在满足如下条件的元素为整数的矩阵
1.矩阵有H行W列;
2.矩阵的每个元素都在-1e9~1e9之间;
3.矩阵全体元素和为正;
4.矩阵的任意h行w列子矩阵元素和为负。
给定H、w,判断是否存在满足条件的矩阵,若存在就构造一个。
分析:存在的充要条件为H%h!=0或W%w!=0。若存在,可以这么构造:取t=(H/h)*(W/w),x=t+1,y=x*(h*w-1)+1。矩阵的第i行第j列当i%h==0且j%w==0时为-y,否则为x。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000;
int H,W,h,w;
int main()
{
cin>>H>>W>>h>>w;
if (H%h==0&&W%w==0)
{
cout<<"No";
return 0;
}
else
{
cout<<"Yes"<<endl;
int t=(H/h)*(W/w);
int x=t+1,y=(t+1)*(h*w-1)+1;
for (int i=1;i<=H;i++)
for (int j=1;j<=W;j++)
{
if (i%h==0&&j%w==0) printf("%d",-y);
else printf("%d",x);
if (j<W) printf(" ");
else printf("\n");
}
}
return 0;
}
D:XOR Replace
题意:给定长度为n的两个序列a、b。可以对a进行如下操作:设x=a1^a2^...^an,选择i∈{1,2,...,n},用x替换ai。问是否能经过有限次操作把序列a变成序列b,若能,给出最少操作次数。
分析:需要注意到如下2个事实:
1.设a0=a1^a2^...^an,那么每次操作相当于交换a0与ai。
因此能经过有限次操作把序列a变成序列b的充要条件是多重集合{a0,a1,...,an}={b0,b1,...,bn}。
2.若答案为能,按如下方式建图G,最少操作次数=G中的边数+G中的极大连通子图数-1。
初始将点a0,b0加入图G。第k步(k=1,2,...,n),若ai不等于bi,将ai,bi加入图G,并加入边ai->bi。
这里解释一下事实2:容易看出,图G的每个连通子图都是欧拉图(即可以“一笔画”),再适当加上G中的极大连通子图数-1条边可使G变成一个欧拉图,根据欧拉路依次交换即可完成把序列a变成序列b这件事,并且显然这样的操作次数是最少的。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,a[maxn],b[maxn],a1[maxn],b1[maxn];
vector<int> G[maxn];
map<int,int> mp;
bool vis[maxn];
void dfs(int u)
{
vis[u]=1;
for (int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if (!vis[v]) dfs(v);
}
}
int main()
{
int col=0;
cin>>n;
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a1[i]=a[i];
a[n+1]^=a[i];
if (!mp[a[i]]) mp[a[i]]=++col;
}
a1[n+1]=a[n+1];
if (!mp[a[n+1]]) mp[a[n+1]]=++col;
for (int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
b1[i]=b[i];
b[n+1]^=b[i];
if (!mp[b[i]]) mp[b[i]]=++col;
}
b1[n+1]=b[n+1];
if (!mp[b[n+1]]) mp[b[n+1]]=++col;
sort(a1+1,a1+n+2);
sort(b1+1,b1+n+2);
for (int i=1;i<=n+1;i++)
if (a1[i]!=b1[i])
{
cout<<"-1";
return 0;
}
for (int i=1;i<=n+1;i++) a[i]=mp[a[i]],b[i]=mp[b[i]];
int ans=0;
for (int i=1;i<=n;i++)
if (a[i]!=b[i])
{
ans++;
G[a[i]].push_back(b[i]);
}
if (a[n+1]!=b[n+1]) G[a[n+1]].push_back(b[n+1]);
for (int i=1;i<col;i++)
if (G[i].size()&&!vis[i])
{
dfs(i);
ans++;
}
if (!vis[col]) ans++;
cout<<ans-1;
return 0;
}
E:Poor Turkeys
题意:略。
分析:设集合S为{1,2,...,n}的子集,下面给出“经过t次选择后集合S中的火鸡都活着(即为事件A(S,t))”的一个判断方式:
1.若xt∈S且yt∈S,则事件A(S,t)不可能发生;
2.若xt∉S且yt∈S,则事件A(S,t)发生当且仅当事件A(S∪{xt},t-1)发生;
3.若xt∈S且yt∉S,则事件A(S,t)发生当且仅当事件A(S∪{yt},t-1)发生;
4.若xt∉S且yt∉S,则事件A(S,t)发生当且仅当事件A(S,t-1)发生。
由此我们可以判断出第i只火鸡是否能在n次选择后活着并且能求出要使第i只火鸡在n次选择后活着在进行选择之前必须活着的火鸡集合Si。可以看出,火鸡i与j在n次选择后能同时活着当且仅当:
1.第i只火鸡在n次选择后能活着;
2.第j只火鸡在n次选择后能活着;
3.Si∩Sj=∅。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=400+10,maxm=1e5+10;
int n,m,x[maxm],y[maxm];
bool ok[maxn],S[maxn][maxn];
bool check(int i,int j)
{
for (int k=1;k<=n;k++)
if (S[i][k]&&S[j][k])
return 0;
return 1;
}
int main()
{
cin>>n>>m;
for (int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]);
for (int i=1;i<=n;i++)
{
ok[i]=1;
S[i][i]=1;
for (int j=m;j>=1;j--)
{
if (S[i][x[j]]&&S[i][y[j]])
{
ok[i]=0;break;
}
if (S[i][x[j]]&&!S[i][y[j]])
S[i][y[j]]=1;
if (!S[i][x[j]]&&S[i][y[j]])
S[i][x[j]]=1;
}
}
int ans=0;
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
if (ok[i]&&ok[j]&&check(i,j))
ans++;
cout<<ans;
return 0;
}