1. 程式人生 > 其它 >2021“MINIEYE杯”中國大學生演算法設計超級聯賽 第三場題解

2021“MINIEYE杯”中國大學生演算法設計超級聯賽 第三場題解

2021“MINIEYE杯”中國大學生演算法設計超級聯賽 第三場題解

賽後深知自己水平提高空間很大

6979Photoshop Layers

題意:

給定一些RGB編碼的顏色,他們有兩種操作:

1:直接覆蓋之前的顏色

2:同之前的顏色進行累加 與255去最小值

他有一堆詢問,詢問 L-R 代表從L顏色到R顏色進行操作後的顏色是什麼

思路:
思考思考我們能夠發現:

1、由1顏色定義可知道如果當前下標R的顏色操作就是1的話,那麼最後的顏色就是R下標顏色

2、根據1我們可知,當前R只會與最近的那個1操作有關,剩下的就是累加2操作了

那麼我們可以對於每一個當前下標idx維護兩個值:

①代表離當前下標idx最近的那個1操作

②當前RGB顏色相對於最近1操作的那個字首和

這樣對於每一個詢問,只需先判斷L和維護的那個idx的位置關係:

①如果L <= idx,說明當前字首和的值就是答案

②否則,需要Sum[R] - Sum[L - 1]進行差分得到一段區間的RGB顏色~(注意相減後需要與255進行比較)

程式碼:

點選檢視程式碼
#include <iostream>
using namespace std;
const int MAXN = 100005;
struct node
{
    int r,g,b;
    int idx;//離他最近的那個m = 1 
    node()
    {r = g = b = 0;}
    node operator - (const node &a)    const
    {
        node now;
        now.r = min(r - a.r,255);
        now.g = min(g - a.g,255);
        now.b = min(b - a.b,255);
        now.idx = idx;
        
        return now;
    }
    void ok()
    {
        r = min(r,255);
        g = min(g,255);
        b = min(b,255);
    }
};
node Get(string s)
{
    node now;
    int x = 0;
    for(int i = 0;i < 6;i+=2){
        int k1,k2;
        if(s[i] >= 'A')
        k1 = s[i] - 'A' + 10;
        else
        k1 = s[i] - '0';
        
        if(s[i + 1] >= 'A')
        k2 = s[i + 1] - 'A' + 10;
        else
        k2 = s[i + 1] - '0';
        
        x = k1*16 + k2;
        if(i == 0)
        now.r = x;
        else if(i == 2)
        now.g = x;
        else if(i == 4)
        now.b = x; 
    }
    return now;
}

void Go(node &a,node b,string s)
{//將b + s放入a中 
    for(int i = 0;i < 6;i+=2)
    {
        int k1,k2;
        if(s[i] >= 'A')
        k1 = s[i] - 'A' + 10;
        else
        k1 = s[i] - '0';
        
        if(s[i + 1] >= 'A')
        k2 = s[i + 1] - 'A' + 10;
        else
        k2 = s[i + 1] - '0';
        
        int x = k1*16 + k2;
        if(i == 0)
        a.r = b.r + x;
        else if(i == 2)
        a.g = b.g + x; 
        else if(i == 4)
        a.b = b.b + x;
    }
}
node sum[MAXN];
void print(node now)
{
    now.ok();
    char ans[10];
    int cnt = 0;
    int x = now.r/16;
    int y = now.r%16;
    if(x >= 10)
    {
        x -= 10;
        ans[++cnt] = 'A' + x;
    }
    else
    ans[++cnt] = x + '0';
    
    if(y >= 10)
    {
        y -= 10;
        ans[++cnt] = 'A' + y;
    }
    else
    ans[++cnt] = y + '0';
    
    x = now.g/16;
    y = now.g%16;
    if(x >= 10)
    {
        x -= 10;
        ans[++cnt] = 'A' + x;
    }
    else
    ans[++cnt] = x + '0';
    
    if(y >= 10)
    {
        y -= 10;
        ans[++cnt] = 'A' + y;
    }
    else
    ans[++cnt] = y + '0';
    
    x = now.b/16;
    y = now.b%16;
    if(x >= 10)
    {
        x -= 10;
        ans[++cnt] = 'A' + x;
    }
    else
    ans[++cnt] = x + '0';
    
    if(y >= 10)
    {
        y -= 10;
        ans[++cnt] = 'A' + y;
    }
    else
    ans[++cnt] = y + '0';
    
    for(int i = 1;i <= cnt;i++)
    printf("%c",ans[i]);
    puts("");
}
char ss[10]; 
int main()
{
    int t;
    scanf("%d",&t);
    sum[0].r = sum[0].g = sum[0].b = 0;
    sum[0].idx = 0;
    while(t--)
    {
        int n,q;
        scanf("%d %d",&n,&q);
        for(int i = 1;i <= n;++i)
        {
            int m;
            string s;
            scanf("%d  %s",&m,ss);
            s = ss;
            if(m == 1)
            {
                sum[i] = Get(s);
                sum[i].idx = i;
            } 
            else
            {
                Go(sum[i],sum[i - 1],s);
                sum[i].idx = sum[i - 1].idx;
            }
        }
        while(q--)
        {
            int le,ri;
            scanf("%d %d",&le,&ri);
            if(sum[ri].idx >= le)
            {//說明就是當前的字首和了 
                print(sum[ri]);
            }
            else
            {//需要相減 
                print(sum[ri] - sum[le - 1]);
            }
        }
    }
    return 0;
}
/*
1
5 5
1 64C832
2 000100
2 010001
1 323C21
2 32C8C8

2 5


*/

QAQ因為是比賽時的程式碼,所以有很多重複的函式塊...(偷懶

6981Rise in Price

題意:

初始化寶石數量以及寶石價格為0。

給你一個寶石數量矩陣和寶石價格矩陣,每當你走到 i , j 位置時你可以拿走那個數字。

求最後你能獲得的最大寶石總價值(即寶石數量×寶石價格)。

你只能從(1,1)位置走到(n,n)位置且每次只能往右走或者往下走。

思路:

如果只有寶石數量這個矩陣的話,那就是一個非常經典的二維DP了...

我們先分析只有一個矩陣的時候的情況...

很顯然,當前狀態(i , j)可以從(i - 1 , j)和(i , j - 1)轉移而來

那麼我們定義的狀態轉移函式就是:

其中f【i,j】表示的是走到當前下標(i , j)處我能獲得的最大寶石數量...

emmmmmm....但是這個題,它多了一個狀態,那就是還有鑽石的價格需要考慮,於是....我們就可以多加入一個狀態,然後再利用上面提到的狀態轉移方程...

由於儲存鑽石數量和鑽石價格的兩個狀態是一致的...現在我們只分析儲存鑽石數量這個狀態的狀態轉移怎麼做

"笑死,多加一維不就成了!"

確實,不過看一眼資料,至少是1e6,得了,需要優化...

不過還是給出這個狀態轉移方程:

這裡F【i,j,k】表示的是當我處在(i , j)位置時,擁有寶石數量為k + a[i][j]時,寶石價值的最大值

那麼怎麼優化呢?我們會發現,這個k其實不是每一個位置的狀態有的,也即1 - MAX(能拿到的最多寶石數)這些狀態有些是取不到的

那麼對於每一個(i , j)我們只用儲存能夠得到的狀態即可....因此這裡的f[i,j]不是使用三維陣列表示,而是使用表(vector or 連結串列)來進行儲存...

我們再仔細觀察...對於狀態(i,j,k1)(i,j,k2)若k1 < k2 且這個狀態(i,j,k1)<(i,j,k2)

那麼k1這個狀態就是最差勁的,因為你不僅寶石數量少,而且價格還低(QAQ

所以這是一個無效狀態...

總歸:

①每次狀態轉移時我們只需要列舉前兩個狀態其中的有效狀態

②對於當前得到的所有狀態(i,j)我們需要將“差勁狀態”剔除,"差勁狀態"滿足k1 < k2 && (i,j,k1)<(i,j,k2)

因此,既然i,j是一個表,何不按照k的遞增順序進行儲存呢,這樣對於每次進來的一個狀態 k'

我們能夠保證k'一定大於k ,下面只需要判斷(i,j,k1)<(i,j,k2)即可

如果k'這個狀態的(i,j,k')大於前面的(i,j,k),就可以將(i,j,k)直接刪除~

這裡可能有點繞,我們講的明白一些:

就是這樣 ->

第一件事,我們假設

其中儲存的都是有效狀態,均是按照k值的大小單調遞增的

第二件事,我們轉移至(i,j)狀態時,由於前兩個狀態都是遞增有序的,那麼我們其實只需要在O(N)的時間內合併兩個陣列即可!

由於a[i][j](寶石數量)和b[i][j](寶石價值)兩個陣列只會影響當前(i,j)狀態,於是合併時並不需要考慮a和b帶來的影響

合併操作:

如果k1 < k2那麼我們肯定優先考慮將k1加入當前(i,j)狀態集

①如果當前(i,j)狀態集沒有狀態...我們直接加入

②否則將狀態集最後那個狀態的(i,j,k')與當前(i - 1,j,k1)進行比較,如果小於它的話,將(i,j,k')刪除(注意!這是一個迴圈的操作

因為當前(i,j)狀態集其中的k'一定是小於k1的!

最後將得到的所有狀態集中k加上a[i][j]以及(i ,j,k)加上b[i][j]即可得到當前(i,j)所有有效狀態~

程式碼:

點選檢視程式碼
#include <iostream>
#include <algorithm>
#include <vector>
#pragma GCC opetimize(2)
using namespace std;
const int MAXN = 107;
typedef long long ll;
struct node{
    int k;//寶石數 
    int w;//權值(賣出的最大價格) 
    node(int k = 0,int w = 0):k(k),w(w){}
    bool operator < (const node &a)    const
    {return k < a.k;}
};
vector<node> dp[MAXN][MAXN];
int a[MAXN][MAXN];
int b[MAXN][MAXN];
/*
2
4
2 3 1 5
6 3 2 4
3 5 1 4
5 2 4 1
3 2 5 1
2 4 3 5
1 2 3 4
4 3 5 3

4
2 3 1 5
6 3 2 4
3 5 1 4
5 2 4 1
3 2 5 1
2 4 3 5
1 2 3 4
4 3 5 3
*/
int main()
{
//    freopen("1009.in","r",stdin);
//    freopen("ans.out","w",stdout);
    int t;
    scanf("%d",&t);
    dp[0][1].push_back(node(0,0));
    dp[1][0].push_back(node(0,0));
    while(t--)
    {
        int n;
        scanf("%d",&n);
        
        for(int i = 1;i <= n;++i)
        for(int j = 1;j <= n;++j)
        scanf("%d",&a[i][j]);
        
        for(int i = 1;i <= n;++i)
        for(int j = 1;j <= n;++j)
        scanf("%d",&b[i][j]);

        for(int i = 1;i <= n;++i)
        for(int j = 1;j <= n;++j)
        {
            dp[i][j].clear();
            int k1 = dp[i - 1][j].size();
            int k2 = dp[i][j - 1].size();
            int i1 = 0,i2 = 0;
            while(i1 < k1 && i2 < k2)
            {
                while(i1 < k1 && dp[i - 1][j][i1].k < dp[i][j - 1][i2].k)
                {
                    while(dp[i][j].size() && dp[i][j].back().w <= dp[i - 1][j][i1].w)
                    dp[i][j].pop_back();
                    
                    dp[i][j].push_back(dp[i - 1][j][i1]);
                    ++i1;
                }
                
                while(i2 < k2 && dp[i][j - 1][i2].k <= dp[i - 1][j][i1].k)
                {
                    while(dp[i][j].size() && dp[i][j].back().w <= dp[i][j - 1][i2].w)
                    dp[i][j].pop_back();
                    
                    dp[i][j].push_back(dp[i][j - 1][i2]);
                    ++i2;
                }
            }
            while(i1 < k1)
            {
                while(dp[i][j].size() && dp[i][j].back().w <= dp[i - 1][j][i1].w)
                dp[i][j].pop_back();
                
                dp[i][j].push_back(dp[i - 1][j][i1]);
                ++i1;
            }
            while(i2 < k2)
            {
                while(dp[i][j].size() && dp[i][j].back().w <= dp[i][j - 1][i2].w)
                dp[i][j].pop_back();
                
                dp[i][j].push_back(dp[i][j - 1][i2]);
                ++i2;
            }
            for(int k = 0;k < dp[i][j].size();k++)
            {
                dp[i][j][k].w += b[i][j];
                dp[i][j][k].k += a[i][j];
            }
//            for(int k = 0;k < dp[i][j].size();k++)
//            printf(">>%d %d -> %d %d\n",i,j,dp[i][j][k].k,dp[i][j][k].w);
            //將i,j中所有 k1 < k2 && w1 < w2的狀態刪除 
        }
        ll ans = 0;
        for(int i = 0;i < dp[n][n].size();i++)
        ans = max(ans,1ll*dp[n][n][i].k*dp[n][n][i].w);
        printf("%lld\n",ans);
    }
    return 0;
}

未完待續...