1. 程式人生 > >codeforces div1 ABC彙總(長期更新)

codeforces div1 ABC彙總(長期更新)

Codeforces Round #310 (Div. 1)

Codeforces Round #309 (Div. 1)

Codeforces Round #305 (Div. 1)

B - Mike and Feet

給出n個數,這n個數在區間長度為i(1~n)的時候可以分割成一些區間,這每個區間都會有一個最小值,在同樣長度的這些區間的最小值中,輸出最大值

思路:用單調棧維護每個元素a[i]作為最小元素的區間。就可以知道對於size=x的區間的最小值是多少。顯然對於答案陣列ans[i],我們可以知道

ans[1]ans[2]ans[n]O(n)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;

int a[maxn], lb[maxn], rb[maxn], ans[maxn];
int main()
{
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++)  scanf("%d", &a[i]);

    {
        stack<int
>
sta; lb[0] = 0, sta.push(0); for(int i = 1; i < n; i++) { while(sta.size() && a[sta.top()] >= a[i]) sta.pop(); lb[i] = sta.size() == 0 ? 0 : sta.top() + 1; sta.push(i); } } { stack<int>sta; rb[n-1
] = n-1, sta.push(n-1); for(int i = n-2; i >= 0; i--) { while(sta.size() && a[sta.top()] >= a[i]) sta.pop(); rb[i] = sta.size() == 0 ? n-1 : sta.top() - 1; sta.push(i); } } for(int i = 0; i < n; i++) { int len = rb[i] - lb[i] + 1; ans[len] = max(ans[len], a[i]); } for(int i = n - 1; i >= 1; i--) ans[i] = max(ans[i], ans[i + 1]); for(int i = 1; i <= n; i++) printf("%d%c", ans[i], i == n ? '\n' : ' '); return 0; }

Codeforces Round #302 (Div. 1)

A - Writing Code(類揹包dp)

問你n個人寫m行程式碼,出的bug的數量少於b個的方案數,每個人寫一行程式碼產生a[i]個bug。(n,m,b500)。

dp[i][j][k]:前i個人寫了j行程式碼一共出了k個bug的方案數。
那麼容易寫出轉移dp[i][j][k]=x=0jdp[i1][jx][kxa[i]]
常規套路化簡成dp[i][j][k] = dp[i-1][j][k] + dp[i][j-1][k-a[i]]即可。O(nmb)

#include <bits/stdc++.h>
using namespace std;

const int maxn = 500 + 5;
long long dp[maxn][maxn];
long long sum[maxn][maxn];
int a[maxn];
int main()
{
    int n, m, b, mod;
    scanf("%d%d%d%d", &n, &m, &b, &mod);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);

    dp[0][0] = 1;

    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            for(int k = a[i]; k <= b; k++)
            {
                dp[j][k] = (dp[j][k] + dp[j-1][k-a[i]]) % mod;
            }
        }
    }
    long long ans = 0;
    for(int i = 0; i <= b; i++)
        ans = (ans + dp[m][i]) % mod;
    printf("%lld\n", ans);

    return 0;
}

B - Destroying Roads(列舉)

給你n個節點,m條邊(權值為1)的無向圖,問你滿足s1到t1經過的路程不超過l1,且s2到t2經過的路程不超過l2的條件下,最多可以刪除幾條邊。(n3000

預處理出兩點間的最短距離,我們直接列舉公共部分,然後計算一下最少可以用幾個。輸出es-ans即可。注意一個可以反向的問題,注意一個ans的初始值應該設定成沒有公共部分的時候的最少使用的邊的數量。O(n2)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3000 + 5;
const int INF = 0x3f3f3f3f;
int dp[maxn][maxn];
int vs, es;
int s1, t1, l1, s2, t2, l2;
vector<int>G[maxn];
void bfs()
{
    for(int st = 1; st <= vs; st++)
    {
        queue<int>que;
        for(int i = 1; i <= vs; i++)    dp[st][i] = INF;

        que.push(st);
        dp[st][st] = 0;
        while(que.size())
        {
            int v = que.front();que.pop();
            for(auto to : G[v])
            {
                if(dp[st][to] == INF)
                {
                    dp[st][to] = dp[st][v] + 1;
                    que.push(to);
                }
            }
        }
    }
}
int main()
{
    scanf("%d%d", &vs, &es);
    for(int i = 0; i < es; i++)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    scanf("%d%d%d%d%d%d", &s1, &t1, &l1, &s2, &t2, &l2);
    bfs();
    if(dp[s1][t1] > l1 || dp[s2][t2] > l2)
    {
        puts("-1");
        return 0;
    }
    int minn = dp[s1][t1] + dp[s2][t2];
    for(int i = 1; i <= vs; i++)
    {
        for(int j = 1; j <= vs; j++)
        {
            int time11 = dp[s1][i] + dp[i][j] + dp[j][t1];
            int time12 = dp[s1][j] + dp[i][j] + dp[i][t1];
            int time21 = dp[s2][i] + dp[i][j] + dp[j][t2];
            int time22 = dp[s2][j] + dp[i][j] + dp[i][t2];
//            printf("dp[%d][%d] = %d\n", i, j, dp[i][j]);
//            printf("%d %d %d %d\n", time11, time12, time21, time22);
            if(time11 <= l1 && time21 <= l2)    minn = min(minn, time11 + time21 - dp[i][j]);
            if(time11 <= l1 && time22 <= l2)    minn = min(minn, time11 + time22 - dp[i][j]);
            if(time12 <= l1 && time21 <= l2)    minn = min(minn, time12 + time21 - dp[i][j]);
            if(time12 <= l1 && time22 <= l2)    minn = min(minn, time12 + time22 - dp[i][j]);

        }
    }
    printf("%d\n", es - minn);


    return 0;
}

C - Remembering Strings (狀壓DP)☆

給了n個長度為m的僅有小寫字母組成的字串,要求把每一個字串變為好記的,定義為存在它的某一位字元在所有字串中那一位獨特,給定變換某個字串某一位的代價,不限制把它轉換成什麼小寫字母,問達到要求的最少的代價是多少(n,m20

由於n,m只有20,小於26。所以將其變換後,一定是滿足條件的,不會重複。那麼考慮
dp[i][j]:前i行,合法的行的狀態記錄為j
有兩種轉移方式,一種是把str[i][j]轉換掉,另一種是把第j列上和str[i][j]相同的x個字元中的x-1個轉換掉。然後預處理出這些需要的變數即可。複雜度O(2nnm)。此時可以通過每次更新S中最低位0的方式,優化掉n的一層迴圈,因為dp[S]不需要從頭開始轉移,他直接從dp[first_zero_of_S]轉移過去即可,那麼和gym上有道掃雷的思路挺像的。因為對列的列舉,只會進行n次,所以幾乎可以看做是O(2nn)

#include <bits/stdc++.h>
using namespace std;

int dp[1 << 20 + 5];
char s[25][25];
int sameCost[25][25], same[25][25], a[25][25], n, m;


int main()
{
    scanf("%d%d", &n, &m);

    for(int i = 0; i < n; i++)  scanf("%s", s[i]);
    for(int i = 0; i < n; i++)
        for(int j = 0; j < m; j++)
            scanf("%d", &a[i][j]);

    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < m; j++)
        {
            int mx = 0;
            for(int k = 0; k < n; k++)
            {
                if(s[i][j] == s[k][j])
                {
                    sameCost[i][j] += a[k][j];
                    mx = max(mx, a[k][j]);
                    same[i][j] |= (1 << k);//state[i][j]: 第j列上 所有和s[i][j]相同字母的 行的記錄。
                }
            }
            sameCost[i][j] -= mx;//v[i][j]:  第j列上 所有和s[i][j]相同字母的 集體修改後使得s[i][j]在j列上唯一的
        }
    }

    //dp[i][j] : 前i行 合法的行的狀態為j時 需要付出的最小花費。

    memset(dp, 0x3f3f3f3f, sizeof(dp));
    dp[0] = 0;

    for(int S = 0; S < (1 <<n); S++)//列舉合法的行的狀態
    {
        for(int i = 0; i < n; i++)//列舉行。找到S最低位的0進行修改。
        {
            if((S & (1 << i)) == 0)// 當第i行並不合法時
            {
                for(int j = 0; j < m; j++)//列舉列
                {
                    //將s[i][j]修改。
                    dp[S | (1 << i)] = min(dp[S | (1 << i)], dp[S] + a[i][j]);
                    //將和s[i][j]相同的修改
                    dp[S | same[i][j]] = min(dp[S | same[i][j]], dp[S] + sameCost[i][j]);
                }
                break;
            }
        }
    }
    printf("%d\n", dp[(1<<n) - 1]);
    return 0;
}