题目大意是给定 n ( 1 ≤ n ≤ 10 ) n(1\le n\le 10) n(1≤n≤10)个敌人的坐标 ( 0 ≤ ∣ x i ∣ , ∣ y i ∣ ≤ 7 ) (0\le|x_i|,|y_i|\le7) (0≤∣xi∣,∣yi∣≤7),每个敌人都有一个被消灭的范围 r i ( 1 ≤ r i ≤ 7 ) r_i(1\le r_i\le7) ri(1≤ri≤7);然后给定攻击者的攻击范围 R ( 1 ≤ R ≤ 7 ) R(1\le R \le7) R(1≤R≤7)以及攻击轮数 k ( 1 ≤ k ≤ 3 ) k(1\le k\le3) k(1≤k≤3),要求确定攻击者的坐标位置(必须是整数),问消灭最多的敌人数。
坐标范围很小,很明显考虑枚举,因为每一次的枚举数到了 15 ∗ 15 = 225 15*15=225 15∗15=225,所以不适合用0/1位枚举,直接DFS即可。血泪教训:不要用sqrt()函数太多次,库函数速度慢,会导致TLE。
#include
#define close ios::sync_with_stdio(false)
using namespace std;
struct Enermy{
int x,y,r;
}e[15];
int n,k,R,maxnum=0;
int vis[15],mp[20][20],dx[5],dy[5];
inline int Distance(int x1,int y1,int x2,int y2){
return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);}
inline void cal()
{
int ans=0;
for(int i=1;i<=n;++i) vis[i]=0;
for(int num=1;num<=k;++num)
for(int i=1;i<=n;++i)
{
if(vis[i] || Distance(dx[num],dy[num],e[i].x,e[i].y)>(R+e[i].r)*(R+e[i].r)) continue;
else vis[i]=1,ans++;
}
if(ans>maxnum) maxnum=ans;
}
inline void DFS(int deep)
{
if(deep>k) {
cal();return;}
else{
for(int i=-7;i<=7;++i)
for(int j=-7;j<=7;++j)
if(mp[i+7][j+7]==0){
mp[i+7][j+7]=1;dx[deep]=i,dy[deep]=j;
DFS(deep+1);
mp[i+7][j+7]=0;
}
}
}
int main()
{
scanf("%d%d%d",&n,&k,&R);
for(int i=1;i<=n;++i) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].r);
DFS(1);
printf("%d",maxnum);
}
#include
using namespace std;
int get_num(int x)
{
int ans=0;
while(x) ans+=x%10,x/=10;
return ans;
}
int main()
{
int n,cur_sum,next,next_sum;cin>>n;
cur_sum=get_num(n);next=n+1;
while(1)
{
next_sum=get_num(next);
if(next_sum==cur_sum) {
cout<<next;break;}
next++;
}
}
题目大意是 n n n个箱子放了 n n n个礼物,有两种操作:①从第 i i i个箱子中取出礼物;②询问 [ x , y ] [x,y] [x,y]中是否有两个相同种类的礼物。
很明显的单点修改和区间查询的问题,但关键问题是如何做到区间查询得到是否有不同种类的礼物。这里是首先采用链表的思想,用 l a s t [ i ] last[i] last[i]表示和第 i i i个礼物种类相同的上一个礼物的位置(没有则为0), n e x [ i ] nex[i] nex[i]表示和第 i i i个礼物种类相同的下一个礼物的位置(没有则为INF)。然后一个礼物的取走,就相当于链表的删除操作: l a s t [ n e x [ i ] ] = l a s t [ i ] , n e x [ l a s t [ i ] ] = n e x [ i ] last[nex[i]]=last[i],nex[last[i]]=nex[i] last[nex[i]]=last[i],nex[last[i]]=nex[i]。我们用 n e x [ ] nex[] nex[]数组建立线段树(区间最小值),很明显一个区间的最小值如果小于区间右端点,就说明存在两个种类相同的礼物。
#include
#define close ios::sync_with_stdio(false)
using namespace std;
const int maxn=5e5+100;
const int INF=0x3f3f3f3f;
vector<int> v[maxn*2];
int n,q,a[maxn],last[maxn],nex[maxn],tree[maxn*4];
void build(int l=1,int r=n,int p=1)
{
if(l==r) tree[p]=nex[l];
else{
int mid=(l+r)/2;
build(l,mid,p*2);build(mid+1,r,p*2+1);
tree[p]=min(tree[p*2],tree[p*2+1]);
}
}
void update(int l,int r,int num,int cl=1,int cr=n,int p=1)
{
if(cr<l || cl>r) return;
if(cl==cr) tree[p]=num;
else{
int mid=(cl+cr)/2;
update(l,r,num,cl,mid,p*2);
update(l,r,num,mid+1,cr,p*2+1);
tree[p]=min(tree[p*2],tree[p*2+1]);
}
}
int query(int l,int r,int cl=1,int cr=n,int p=1)
{
if(cr<l || cl>r) return INF;
if(cl>=l && cr<=r) return tree[p];
else{
int mid=(cl+cr)/2;
return min(query(l,r,cl,mid,p*2),query(l,r,mid+1,cr,p*2+1));
}
}
int main()
{
close;cin>>n>>q;
for(int i=1;i<=1e6;++i) v[i].push_back(0);
for(int i=1;i<=n;++i) cin>>a[i],v[a[i]].push_back(i);
for(int i=1;i<=1e6;++i)
{
int size=v[i].size();
if(size==1) continue;
v[i].push_back(INF);
for(int j=1;j<size;++j) last[v[i][j]]=v[i][j-1],nex[v[i][j]]=v[i][j+1];
}
build();
while(q--)
{
int op;cin>>op;
if(op==1){
int pos;cin>>pos;
if(nex[pos]!=INF) last[nex[pos]]=last[pos];//注意这个地方要特判,否则会造成越界
nex[last[pos]]=nex[pos];
update(pos,pos,INF);
update(last[pos],last[pos],nex[pos]);
last[pos]=0;nex[pos]=INF;
}
else{
int x,y;cin>>x>>y;
if(query(x,y)<=y) cout<<"1\n";
else cout<<"0\n";
}
}
}
题目大意是给定 n n n个匹配串,仅由小写字母或者’#‘组成,’#‘代表能够匹配任意长度的序列,同时保证了每个匹配串中至少含有一个’#’。问能不能找到字符串和这 n n n个匹配串都匹配,如果不能则输出0,有无穷多个则输出-1,否则输出个数。
简单的贪心题,只要每个匹配串的第一个’#‘前的前缀串和最后一个’#'后的后缀串能够能够匹配(不一定长度一样,但是没有出现不一致),就能够构造出无穷多个,否则输出-1.
#include
using namespace std;
const int maxn=1e6+100;
string s[maxn],before="",after="";bool ok=true;
void solve(int n)
{
for(int i=0;i<s[1].length();++i) {
if(s[1][i]!='#') before+=s[1][i];else break;}
for(int i=s[1].length()-1;i>=0;--i){
if(s[1][i]!='#') after+=s[1][i];else break;}
int lenb=before.length(),lena=after.length();
for(int i=2;i<=n;++i)
{
int p=0;
for(int j=0;j<s[i].length();++j)
{
if(s[i][j]!='#'){
if(p<lenb){
if(before[p]!=s[i][j]) {
ok=false;return;}p++;}
else before+=s[i][j],p=++lenb;
}
else break;
}
p=0;
for(int j=s[i].length()-1;j>=0;--j)
{
if(s[i][j]!='#'){
if(p<lena){
if(after[p]!=s[i][j]) {
ok=false;return;}p++;}
else after+=s[i][j],p=++lena;
}
else break;
}
}
}
int main()
{
int n;cin>>n;
for(int i=1;i<=n;++i) cin>>s[i];
solve(n);
if(ok) cout<<-1;else cout<<0;
}
题目大意是给 n n n个小朋友分糖,他们有 m m m对朋友关系,要同时满足①不能低于 a i a_i ai;②分的躺不能比他朋友分的少。
可以转化成图,有朋友关系就是有一条边相连,每个小朋友分的糖果数就是其所在的连通块中最大的 a i a_i ai.所以DFS跑一遍图,找到图中的各个连通块的大小以及连通块中 m a x ( a i ) max(a_i) max(ai)即可。
#include
#define close ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
const int maxn=1e6+100;
vector<int> v[maxn];int vis[maxn],a[maxn];
ll ans=0,num=0,maxnum=0;
void DFS(int root,int fa)
{
int size=v[root].size();
num++;vis[root]=1;if(a[root]>maxnum) maxnum=a[root];
for(int i=0;i<size;++i)
{
if(v[root][i]==fa || vis[v[root][i]]) continue;
DFS(v[root][i],root);
}
}
int main()
{
close;int n,m;cin>>n>>m;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=m;++i){
int x,y;cin>>x>>y;
v[x].push_back(y);v[y].push_back(x);
}
for(int i=1;i<=n;++i)
if(!vis[i]) num=maxnum=0,DFS(i,0),ans+=num*maxnum;
cout<<ans;
}
题目大意是给出一个只包括小写字母的字符串S,并假定’a’=1,‘b’=2…,‘z’=26.你需要找出另一个字符串T,满足按照上述规则转换出来的数字串和S转换出来的数字串保持一致。多种方案输出一种即可,不存在则输出-1.
①拆分:存在一个字母对应的数字比10大,同时不等于20时,我们将个位和十位拆开,分别转换成一个字母即可;②合并:如果两个字母对应的数字都是一位数,同时组合起来是一个不超过26的合法数字,那么就直接组合转换成一个字母。
如果上述两种方式都做不到,说明没有合法的字符串,输出-1.
#include
#define close ios::sync_with_stdio(false);
using namespace std;
bool ok=false;
int main()
{
string T,S="";cin>>T;
int len=T.length();
for(int i=0;i<len;++i)
{
int num=T[i]-'a'+1;
if(num>10 && num!=20){
S+=(char)((num/10)-1+'a');S+=(char)((num%10)-1+'a');
for(int j=i+1;j<len;++j) S+=T[j];
ok=true;
break;
}
else if(num<=2){
if(i+1<len && (T[i+1]-'a'+1)<10 && num*10+(T[i+1]-'a'+1)<=26)
{
S+=(char)('a'+num*10+(T[i+1]-'a'));
for(int j=i+2;j<len;++j) S+=T[j];
ok=true;
break;
}
}
S+=T[i];
}
if(ok) cout<<S;else cout<<-1;
}
题目大意是一个字符串的美观度是整数 i i i的数量,整数 i ( 1 ≤ i ≤ n ) i(1\le i\le n) i(1≤i≤n)要满足 S i = S i + 1 S_i=S_{i+1} Si=Si+1.问对于给定的字符串S来说,他的所有子序列(不一定连续)中最大的美观度是多少。
解法一:贪心的思想,找最近的两个相同数字拼接在当前序列末尾是最优的。设 x , y x,y x,y是当前最近的两个相同数字,那么在 x , y x,y x,y之间一定找不到任何相同的两个数字;假定 z z z是中间的某一个数,那么即使他和 y y y后面的某个数配对,贡献也不会超过当前的选择(都是1)。
#include
#define close ios::sync_with_stdio(false)
using namespace std;
const int maxn=1e6+100;
int loc[maxn],a[maxn];
map<int,int> mp;
int main()
{
close;int n,x;cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
if(mp[a[i]]!=0) loc[i]=mp[a[i]];
mp[a[i]]=i;
}
int lastpos=1,num=0;
for(int i=1;i<=n;++i)
if(loc[i]>=lastpos) lastpos=i,num++;
cout<<num;
}
赛后看到了题解中的一种做法。解法二:DP。我们令 d p [ i ] dp[i] dp[i]表示以 a [ i ] a[i] a[i]结尾的子序列的最大美观度,很容易得到转移方程是: d p [ i ] = m a x { d p [ j ] + 1 } ( j < i & & a [ i ] = a [ j ] ) dp[i]=max\{dp[j]+1\}(jdp[i]=max{ dp[j]+1}(j<i && a[i]=a[j]) d p [ i ] = m a x { d p [ j ] } ( j < i & & a [ i ] ≠ a [ j ] ) dp[i]=max\{dp[j]\}(jdp[i]=max{ dp[j]}(j<i && a[i]=a[j])如果直接这样写的时间复杂度是 O ( n 2 ) O(n^2) O(n2),但我们可以再第一个式子中就记录最近一个相等的位置,第二个式子直接对前 i − 1 i-1 i−1项记录最大值,然后二者区最大即可。
题目大意是给定 n n n个数,每次可以选择其中的两个数,将他们进行加法或乘法运算,然后仅保留运算后的结果。最后剩下一个数时,如果是奇数,则牛牛赢;否则牛妹赢。
如果 n = 1 n=1 n=1,直接判断结果; n = 2 n=2 n=2时,两个数都是偶数时,牛妹赢,否则都是牛牛赢; n ≥ 3 n\ge 3 n≥3时,①如果偶数的个数多余1个,一定是牛妹赢:我们发现牛牛每次操作最多减少一个偶数,但牛妹可以减少两个奇数同时带来一个偶数,不管牛牛怎么操作,每一轮牛妹都选择两个奇数相加就可以维持偶数个数最少不改变,两个偶数朝上和一个奇数的局面一定是牛妹赢。②如果只有一个偶数,那就相当于牛妹先手。③奇数个奇数时,谁后手谁赢;④偶数个奇数时,谁先手谁赢。
#include
#define close ios::sync_with_stdio(false)
using namespace std;
const int maxn=1e6+100;
int a[maxn];
int main()
{
close;int n,num_even=0,num_odd=0;cin>>n;
for(int i=1;i<=n;++i)
cin>>a[i],(a[i]&1)?num_odd++:num_even++;
if(n==1){
if(num_odd==1) cout<<"NiuNiu";else cout<<"NiuMei";}
else if(n==2){
if(num_even==2) cout<<"NiuMei";else cout<<"NiuNiu";}
else{
if(num_even>=2) cout<<"NiuMei";
else if(num_even==1){
if(num_odd&1) cout<<"NiuNiu";else cout<<"NiuMei";}
else{
if(num_odd&1) cout<<"NiuMei";else cout<<"NiuNiu";}
}
}