A - Leftrightarrow(A - Leftrightarrow)
#include<bits/stdc++.h>
using namespace std;
int main()
{
string s;
cin>>s;
int l=s.size()-1;
if(s[0]=='<'&&s[l]=='>')
{
int flag=1;
for(int i=1;i<l;i++)
{
if(s[i]!='=')
{
flag=0;
break;
}
}
if(flag) printf("Yes\n");
else printf("No\n");
}
else printf("No\n");
}
B - Integer Division Returns(B - Integer Division Returns)
直接通过最后一位是否为0来判断,注意正负不同
#include<bits/stdc++.h>
using namespace std;
int main()
{
long long x;
scanf("%lld",&x);
if(x>=0)
{
if(x%10) cout<<x/10+1;
else cout<<x/10;
}
else
{
cout<<x/10;
}
}
C - One Time Swap(C - One Time Swap)
可以通过遍历来找每个字符前面有多少个和它不同的字符,两者一旦交换就产生新字符串,然后顺便统计一下,某个字符前面是否有与之相同的字符,如果有,最后的结果+1,表示两者交换,字符串不变的情况。
#include<bits/stdc++.h>
using namespace std;
int st[30];
int main()
{
string s;
cin>>s;
long long ans=0,flag=0;
for(int i=0;i<s.size();i++)
{
int d=s[i]-'a';
for(int j=0;j<26;j++)
{
if(j!=d) ans+=st[j];
else
{
if(st[j]) flag=1;
}
}
st[d]++;
}
printf("%lld",ans+flag);
}
D - Tiling(D - Tiling)
从n块瓷砖中选择一部分,使得被选瓷砖完全覆盖h*w的矩形,问能否实现。
思路这题的数据比较小,可以暴力。即我们从左往右一排一排的找,找到矩阵中第一个没放瓷砖的地方,枚举没用过的瓷砖,看看是否有左上角可以放在这个位置的瓷砖,如此搜索就可实现。
#include<bits/stdc++.h>
using namespace std;
int sta[20][20];
int st[30];
int a[30],b[30];
int n,h,w;
int ans;
void dfs(int x,int y)
{
while(sta[x][y])
{
y++;
if(y>w)
{
x++;
y=1;
}
if(x>h) break;
}
if(x>h) //x大于h,那么就说明前h行都填满了
{
ans = 1;
return ;
}
for(int i=1;i<=n;i++)
{
if(st[i]) continue;
int flag=1;
for(int ix=0;ix<a[i];ix++)
for(int iy=0;iy<b[i];iy++)//左上的格子直接填
if(x+ix<=h&&y+iy<=w)
{
if(!sta[x+ix][y+iy]) sta[x+ix][y+iy] = i;
else
{
flag=0;
break;
}
}
else
{
flag=0;
break;
}
if(flag)
{
st[i]=1;
dfs(x,y);
st[i]=0;
}
for(int ix=0;ix<a[i];ix++)
for(int iy=0;iy<b[i];iy++)
if(x+ix<=h&&y+iy<=w)
if(sta[x+ix][y+iy]==i) sta[x+ix][y+iy] = 0;
if(a[i]!=b[i])
{
flag=1;
for(int ix=0;ix<a[i];ix++)
for(int iy=0;iy<b[i];iy++)
if(x+iy<=h&&y+ix<=w)
{
if(!sta[x+iy][y+ix]) sta[x+iy][y+ix] = i;
else
{
flag=0;
break;
}
}
else
{
flag=0;
break;
}
if(flag)
{
st[i]=1;
dfs(x,y);
st[i]=0;
}
for(int ix=0;ix<a[i];ix++)
for(int iy=0;iy<b[i];iy++)
if(x+iy<=h&&y+ix<=w)
if(sta[x+iy][y+ix]==i) sta[x+iy][y+ix] = 0;
}
}
return;
}
int main()
{
scanf("%d%d%d",&n,&h,&w);
for(int i=1;i<=h;i++)
for(int j=1;j<=w;j++)
sta[i][j]=0;
for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);
dfs(1,1);
if(ans) printf("Yes\n");
else printf("No\n");
}
E - Colorful Subsequence(E - Colorful Subsequence)
有一排彩色气球,共有n,每个气球有一个颜色和价值,我们需要从中删除k个气球,使得相邻两个气球颜色不同,且剩下气球的价值最大。
思路:我最开始想的是将重复部分只留一个,统计要删多少个,如果删除的数目小于k的话,再将剩下的里面小的删除。但是对于aabaa这种情况,如果要删3个,且b最小,那么就会出现aa的情况,显然不可以。
这题用的是dp。
先看暴力层面定义:dp[i][j][k]表示前i个中,删除j个,结尾颜色是k的最大价值。
对于第i个,我们有留与不留两种选择
如果留,dp[i][j][k]=max(dp[i-1][j][k'])+vi//从前i-1中选,删除j个,结尾为k'(k'不等于k)
如果不留,dp[i][j][k]=max(dp[i-1][j-1][k'])如果不留,就可以从各种颜色中找最值
注意到这里有一种特殊情况,j=0,对于这种情况,i必须留,因为没有操作可以用来删它,那么一旦遍历到不能留的位置,赋成负无穷即可。
上面的方法虽好,但是显然时间复杂度和空间复杂度都太高了,我们观察可以发现实际上用到的就是(i-1,j)和(i-1,j-1)在不同颜色下的最值,那么我们是否可以将这个最值维护出来?显然是可以的,不过要注意,我们需要考虑结尾的颜色,所以最大值不一定能用,故而应该同时维护一个次大值,一定要保证最大值和次大值的颜色不同,将这两个值和两种颜色记一下,然后不断遍历,不断更新即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int dp[200010][2][2];
const int inf=2e18+10;
signed main()
{
int n,k;
scanf("%lld%lld",&n,&k);
//第二维 : 0表示最大值,1表示次大值
//第三维:0表示颜色,1表示值
for(int i=0;i<=k;i++)
{
dp[i][0][0]=-1;
dp[i][0][1]=-inf;
dp[i][1][0]=-1;
dp[i][1][1]=-inf;
}
dp[0][0][0]=0,dp[0][0][1]=0;
for(int i=1;i<=n;i++)
{
int c,v;
scanf("%lld%lld",&c,&v);
for(int j=k;j>=1;j--)
{
//保留
if(dp[j][0][0]!=c)
{
dp[j][0][0]=c;
dp[j][0][1] += v;
}
else
{
dp[j][0][0]=c;
dp[j][0][1]=dp[j][1][1]+v;
}
//保留的时候已经用过上一层j的值了,后面不需要再用到了,而且不修改会影响判断
//最大值和次大值对应的颜色不能相同,所以保留的情况已经更新过了,下面需要更新不保留的情况
dp[j][1][0]=-1;
dp[j][1][1]=-inf;
for(int kk=0;kk<=1;kk++)
{
if(dp[j-1][kk][1]> dp[j][0][1])
{
if(dp[j-1][kk][0]!=dp[j][0][0])
{
dp[j][1][0]=dp[j][0][0];
dp[j][1][1]=dp[j][0][1];
}
dp[j][0][0]=dp[j-1][kk][0];
dp[j][0][1]=dp[j-1][kk][1];
}
else if(dp[j-1][kk][1]> dp[j][1][1]&&dp[j-1][kk][0]!=dp[j][0][0])
{
dp[j][1][0]=dp[j-1][kk][0];
dp[j][1][1]=dp[j-1][kk][1];
}
}
}
if(dp[0][0][0]!=c)
{
dp[0][0][0]=c;
dp[0][0][1] += v;
}
else dp[0][0][1] = -inf;
}
if(dp[k][0][1]<0) printf("-1");
else printf("%lld",dp[k][0][1]);
//和最长上升子序列有点像,但是最长上升子序列不知道删除个数,这题又限定了删除个数
}
F - Many Lamps(F - Many Lamps)
有n个灯泡和m条边,每条边连接两个灯,我们任意选边,然后将边上开着的灯关掉,关着的灯打开,问最后能否恰好有k个灯打开。
思路:我们考虑下操作实际的贡献:
00->11 +2
01->10 +0
10->01 +0
11->00 -2
所以变化要么是2,要么不变,那么就只有k为偶数才有答案,否则就无解。
那么具体该怎么解呢?我们来考虑开灯的顺序,假设选了1-2,又选了2-3,显然此时只有两个灯亮着,我们再选4-2,就有4个灯亮着。所以,我们来考虑一下,该怎么打开尽可能多的灯呢?图比较麻烦的话,我们去思考对一个树的情况。我们从叶子节点开始一层一层往上的话,开的灯会多一些,因为如果先开了父节点,那么开叶子节点的时候,父节点的状态就是不确定的了,但如果先开了子节点,那么用父节点的父节点去开的时候就不会影响子节点。
#include<bits/stdc++.h>
using namespace std;
int h[200010],e[400010],ne[400010],idx;
int st[200010],lamb[200010];
int n,m,k,cnt;
vector<int>q;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int fa)
{
st[u]=1;
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
if(st[j]||j==fa) continue;
dfs(j,u);
if(lamb[j]==0&&cnt<k)
{
cnt -= lamb[j]+lamb[u];
lamb[u]^=1,lamb[j]^=1;
cnt += lamb[j]+lamb[u];
//0 1
q.push_back(i/2+1);
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
for(int i=1;i<=n;i++)
{
if(!st[i]) dfs(i,i);
}
if(cnt==k)
{
printf("Yes\n");
cout<<q.size()<<endl;
for(auto it:q)
{
cout<<it<<" ";
}
}
else printf("No\n");
}