1. 程式人生 > >Codeforces Round #459 (Div. 2)題解

Codeforces Round #459 (Div. 2)題解

urn 不用 blog 所有 truct 不存在 pac namespace edge

補題

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("");
    }
}

Codeforces Round #459 (Div. 2)題解