前言
在家闲着没事打牛客,发现自己被虐了。。。
好像别人的做法都很妙的样子。。。
A. 括号串
模拟题。
判断所给字符串合法等价于:
- 括号完全匹配
A
串是B
串的子序列
题意很扯淡。我最开始看到字符串长度可以比最小的小,可以删除 qwq
。
#include<bits/stdc++.h>
#define PII pair<int,int>
using namespace std;
const int mx=1e6+5;
int n,m,s[mx],cnt;
char a[mx],b[mx];
bool match(char x,char y) {
return x=='('&&y==')'||x=='['&&y==']';
}
int main() {
int T; scanf("%d",&T);
while(T--) {
scanf("%d%d%s%s",&n,&m,a+1,b+1);
cnt=0;
for(int i=1;i<=m;i++) {
if(b[i]=='['||b[i]=='(') {
s[++cnt]=i;
}
else {
if(cnt&&match(b[s[cnt]],b[i])) {
cnt--;
}
else {
cnt++;
break;
}
}
}
if(cnt) {
printf("Wrong Answer\n");
continue;
}
int j=1;
for(int i=1;i<=m;i++) {
if(j<=n&&a[j]==b[i]) j++;
}
if(j==n+1) {
printf("Accepted\n");
}
else {
printf("Wrong Answer\n");
}
}
}
B. 虎龙斗
被虐了 qwq
。
首先想贪心做法,枚举 A
中选几个, B
中选 l-i
个,然后归并排序合并就完了 qwq
。然而这种贪心是错的,如下数据 :
3 3 6
1 1 1
1 1 2
显然先选下面的序列更优,然而我粗略地选了前一个就跑了导致只有 60pts
。 这个时候贪心做不出来就自闭了,然后想 dp
,发现状态设计不出来。。。
后来看到一堆 O(n^2logn)
的线段树做法,自己也不会就只会 O(n^3)
的大众做法了:
仍然考虑上述贪心,有结论:选取字典序较大的那个一定更优(假若当前位相同)。
例子:
3 3 6
1 3 1
1 3 2
证明也很简单。考虑不同的那一位,假设 A<B
,如果 A
在 B
前面,那么调换两个字符串位置显然不会有影响,保证 B
在 A
前面且答案更大,所以 A
只能在 B
后面,那么一定存在一个时刻两个串选了相同的前缀,而这是包含在先选 B
的决策中的。
#include<bits/stdc++.h>
#define PII pair<int,int>
#define INF 0x3f3f3f3f
using namespace std;
//思路:贪心 + dp
//我太菜了 qwq...
int n,m,l,A[505],B[505],E[505],F[505],G[1005],H[1005],s[505],cnt;
vector<int> C[505],D[505];
bool chk(int *G,int *H) {
for(int i=1;i<=l;i++) {
if(G[i]!=H[i]) return G[i]>H[i];
if(G[i]==-INF&&H[i]==-INF) return 0;
}
return 0;
}
int main() {
memset(B,-0x3f,sizeof(B));
memset(A,-0x3f,sizeof(A));
memset(H,-0x3f,sizeof(H));
scanf("%d%d%d",&n,&m,&l);
for(int i=1;i<=n;i++) scanf("%d",&A[i]);
for(int i=1;i<=m;i++) scanf("%d",&B[i]);
for(int i=1;i<=n;i++) {
int k=n-i; cnt=0;
for(int j=1;j<=n;j++) {
while(k&&cnt&&A[s[cnt]]<A[j]) {
cnt--,k--;
}
s[++cnt]=j;
}
for(int j=1;j<=i;j++) {
C[i].push_back(A[s[j]]);
}
}
for(int i=1;i<=m;i++) {
int k=m-i; cnt=0;
for(int j=1;j<=m;j++) {
while(k&&cnt&&B[s[cnt]]<B[j]) {
cnt--,k--;
}
s[++cnt]=j;
}
for(int j=1;j<=i;j++) {
D[i].push_back(B[s[j]]);
}
}
for(int i=0;i<=l;i++) {
if(i>n||l-i>m) continue;
memset(E,-0x3f,sizeof(E)),memset(F,-0x3f,sizeof(F));
int j,k,r;
for(j=1;j<=i;j++) E[j]=C[i][j-1];
for(k=1;k<=l-i;k++) F[k]=D[l-i][k-1];
j=k=r=1;
while(j<=i&&k<=l-i) {
if(E[j]>F[k]) G[r++]=E[j++];
else if(E[j]<F[k]) G[r++]=F[k++];
else if(chk(E+j,F+k)){
G[r++]=E[j++];
}
else {
G[r++]=F[k++];
}
}
while(j<=i) G[r++]=E[j++];
while(k<=l-i) G[r++]=F[k++];
if(chk(G,H)) memcpy(H,G,sizeof(G));
}
for(int i=1;i<=l;i++) printf("%d ",H[i]);
}
C. 马老师
良心出题人。。。
不妨把二进制每一位 i
拆开考虑,发现就是一个裸的背包问题。
发现可以用前缀和优化,时间复杂度 O(nlogn)
。
#include<bits/stdc++.h>
#define PII pair<int,int>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int mx=5e6+5;
const int mod=1e9+7;
//背包问题
//前缀和优化,时间复杂度 O(nlogn)
int n,m;
int dp[mx],sum[mx],res;
int main() {
scanf("%d%d",&n,&m);
dp[0]=sum[0]=1;
for(int i=0;i<25;i++) {
for(int j=(1<<i);j<=m;j++) {
sum[j]=(sum[j-(1<<i)]+dp[j])%mod;
}
for(int j=(1<<i);j<=m;j++) {
if(j<1ll*(n+1)*(1<<i)) dp[j]=(dp[j]+sum[j-(1<<i)])%mod;
else dp[j]=(dp[j]+sum[j]-sum[j-(n+1)*(1<<i)])%mod;
if(dp[j]<0) dp[j]+=mod;
}
if((1<<i+1)>m) {
break;
}
}
printf("%d",dp[m]);
}
D. 渡摆车
话说是增广路算法。。。
咕咕咕