D. Dr. Evil Underscores
原题地址
代码:
题目大意:给n个数字,让你求一个数X,使得X和每个数的异或值的最大值最小
其实看到最大值最小这样的表述很容易向二分方向想,但显然这题没法二分,只能字典树+DP
先将每个数变成30位的二进制数,然后建立字典树,在字典树上搞树上DP
qsc讲解、
qsc的代码省略了建树的过程(一边DP一边建)
E. Xenon’s Attack on the Gangs
原题地址
代码:
题目大意:给一棵树的每个边赋一个权值(0到n-2),定义一个路径的值为这条路径所有边中没出现的最小自然数,使得所有路径之和最少。
显然,把比较小的权全都放在一起比较好
qsc视频讲解
有意的地方在于,DP的过程,当i与j不相邻时,dp[i][j]=-1,而当中间的链都推完时,则可以运行到求dp[j][i],两者等价
E. Erase Subsequences
原题地址
代码:
题目大意:给一个s串和一个t串,先提取s的一个子序列组成t1串,然后在剩下的里面提取一个t2,问t1和t2能否组成t串
qsc视屏讲解
正反则逆,我们考虑将t化成两个字符串,去寻找匹配的s子序列
现将t分成两个子串,然后去匹配s;
一个神奇的DP,dp[i][j]表示s取到第i个字符,t1取到第j个字符所能匹配到的t2的最大值
//代码摘自qsc视频讲解
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<vector>
using namespace std;
typedef long long ll;
#define maxn 405
#define pi 3.1415926
const int mod=1e9+7;
int dp[maxn][maxn];
bool check(string s,string t)//先判断一个子串能不能解决
{
int idx=0;
if(!t.size()) return true;
for(int i=0;i<s.size();i++)
{
if(s[i]==t[idx]) idx++;
if(idx==t.size()) return true;
}
return false;
}
bool check(string s,string t1,string t2)
{
memset(dp,-1,sizeof(dp));
dp[0][0]=0;
for(int i=0;i<s.size();i++)
{
for(int j=0;j<=t1.size();j++)
{
if(dp[i][j]>=0)
{
if(j<t1.size()&&t1[j]==s[i])//与t1匹配
{
dp[i+1][j+1]=dp[i][j];
}
if(dp[i][j]<t2.size()&&t2[dp[i][j]]==s[i])//与t2匹配
{
dp[i+1][j]=max(dp[i+1][j],dp[i][j]+1);
}
dp[i+1][j]=max(dp[i+1][j],dp[i][j]);
}
}
}
int res1=dp[s.size()][t1.size()];//最大匹配
int res2=t2.size();
if(res1>=res2) return true;
else return false;
}
void solve()
{
string s,t;
cin>>s>>t;
if(check(s,t))
{
cout<<"YES"<<endl;
return;
}
for(int i=0;i<t.size()-1;i++)//枚举切割位置
{
string t1="",t2="";
for(int j=0;j<=i;j++) t1+=t[j];
for(int j=i+1;j<t.size();j++) t2+=t[j];
bool res=check(s,t1,t2);
if(res)
{
cout<<"YES"<<endl;
return;
}
}
cout<<"NO"<<endl;
return;
}
int main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
{
solve();
}
return 0;
}