补题
codeforces 918C
题意
给定一个含有通配符?
和()
的字符串,问有多少子串是括号匹配的
解题思路
首先考虑不用栈求括号匹配的方法:
bool solve(char* s)
{
int top=0;
for (int i=0;i<strlen(s);i++)
{
if(s[i]==‘(‘) top++;
else top--;
if(top<0) return false;
}
return top==0;
}
由于在使用栈的括号匹配算法中,栈内是不存在)
的,所以不难证明这个算法和使用栈的括号匹配算法是等价的
我们维护一个\(top\)值的上确界\(sup\)和下确界\(inf\),如果维护过程\(inf<0\),那么对于所有后续的串的合法通配方案,都至少需要让这段的一个?
变成(
,我们不妨让下界变成\(0\)。那么每次串长度为偶数并且区间存在能组成\(0\)的方案(即\(inf=0\))即说明子串是可以括号匹配的
复杂度\(O(n^2)\)
AC代码
#include <bits/stdc++.h>
using namespace std;
char str[5020];
int main()
{
scanf("%s",&str);
int len=strlen(str);
int ans=0;
for (int i=0;i<len;i++)
{
int sup=0,inf=0;
for (int j=i;j<len;j++)
{
if(str[j]==‘(‘) inf++,sup++;
else if(str[j]==‘)‘) sup--,inf--;
else inf--,sup++;
if(sup<0) break;
if(inf<0) inf=0;
if((j-i+1)%2==0&&inf==0) ans++;
}
}
printf("%d\n",ans);
}
codeforces 918D
题意
给定一个有向无环图(\(DAG\)) \(G\),边上有用字母表示的权值,权值的大小关系由\(ASCII\)码决定。玩家\(A\)和\(B\)进行博弈,\(A\)先手,\(B\)后手,两名玩家各有一个棋子,每一轮将棋子从图上的一个点沿着有向边移动到另一个点,随后移动权转给另一名玩家。
规定:每一轮走的边的权值必须大于等于上一次走的权值
胜利条件:当有一名玩家无法走时,另一名玩家获得胜利
输出\(n\)行\(n\)列,其中\(n\)为图的点数,假设两名玩家都是绝对理性的,第\(i\)行\(j\)列输出获胜的玩家(\(A/B\))
\(n \le 100\)
解题思路
对于这类的组合游戏,我们可以知道:
一个状态是必败状态当且仅当它的所有后继都是必胜状态或者没有后继
一个状态是必胜状态当且仅当它至少又一个后继是必败状态
那么对于此题,我们不妨用dp[u][v][c]
表示\(A\)在\(u\),\(B\)在\(v\),上一轮移动边的权值为\(c\)的状态,如果存在边\(e\),并且权值\(\ge c\),那么它可以转移到dp[v][e.to][e.c]
(等效于从\(B\)开始先手)
进而根据上面的准则判断这个状态是否是必胜/败状态即可,答案是dp[i][j][0]
实现上可以使用记忆化搜索
粗略估计最坏情况每次都要遍历一次图,复杂度\(O(n^4)\),实际的上界大概要再低一些,因为不可能每次都要遍历图
AC代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=102;
struct edge
{
int to,cost;
edge(int t,int c):to(t),cost(c){
}
bool operator < (const edge &rhs) const
{
return cost<rhs.cost;
}
};
vector <edge> G[maxn];
int dp[maxn][maxn][28];
int solve(int u,int v,int c)
{
if(dp[u][v][c]!=-1)
return dp[u][v][c];
dp[u][v][c]=0;
for (edge &e: G[u])
{
if(e.cost<c) continue;
dp[u][v][c]|=(!solve(v,e.to,e.cost));
}
return dp[u][v][c];
}
int main()
{
int m,n;
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int u,v;
char c;
scanf("%d%d",&u,&v);
cin>>c;
G[u].push_back(edge(v,c-‘a‘+1));
}
memset(dp,-1,sizeof(dp));
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
if(solve(i,j,0))
printf("A");
else
printf("B");
puts("");
}
}