ZOJ 3352-3361||Monthly July 2010

按照難度從簡單到難。不過榜歪了,準確來說是按照過題人數從多到少。

D題:題意是有三種賠率,現在問你有沒有方案使得無論哪種結果出現都可以賺錢?

一開始的思路特別蠢,但是我忘記是啥了,等我明天去翻翻草稿紙再回來寫。

翻到了。TAT 就是窮舉方案,然後算出每個錢數用了多少,直接找規律,找了半天都沒算出錢數要用多少……

其實設三個未知數列出不等式,就能知道。只要1/a+ 1/b+ 1/c 小於1就一定可以賺錢了,直接這樣寫會wa,記得要加上浮點誤差的判斷。或者是因爲只有兩位小數,直接把浮點數乘以一百然後加上一個EPS轉換成整數就可以了。


H題:模擬題。我的姿勢被批評很扭曲,但是一炮過,我很開心~~~~


E題:給你一些錢,讓你怎麼分三種賠率得到的錢最多。本來是三千毫秒,然後想着10的八次方暴力,然後循環裏面大概有五十多步,TAT,WA了。

然後我仔細研究了樣例。發現a的個數是(bc)* n/(ab+bc + ac)醬紫的,然後就做了,然而wa了,正確的做法應該是算出這個i之後往前延伸兩個往後延伸兩個,也就是i-2遍歷到i+2.那麼爲什麼要枚舉呢?首先要先知道,爲什麼a的個數是(bc)* n/(ab+bc + ac)醬紫呢??晚上再寫


A題:題意是有一個有向圖,有“一面!!!”白旗和“一面!”黑旗。每人可以移動旗子,不能移動算輸。問你先手想要收益最大,第一步有幾種方案。

想起那個異或爲0的也是問第一步有幾種方案,當時推了一整場比賽。還有那個sg函數真的要好好看看了!!!!!然而和這一道題沒啥關係。

這題就是用三維dp[i][j][k],i表示當前白旗位置,j表示當前黑旗位置,k表示我往下搜索之前場上的錢數,dp值存的是搜索結束後最大的收益。

對白旗跑一遍dfs,對黑旗跑一遍dfs。

至於方案數的判斷,我原來用的做法是在main裏面判斷幾種方案然後算,然而這樣的話相當於轉換了先手作爲後手,會wa。要直接用記憶化搜索檢查第一個人,同時加一個判斷是否是第一步。

dp如果更新過就不用更新。

注意會出現負數。然後我開了大概8*(10^6)的數組。然後所有dp下標加上一個更正值就好啦。

神奇的事情是調用了dp[i][j][-1]居然不會報錯。而是=0,簡直神奇。

/*
dp[i][j][k]表示白棋在i,黑棋在j,然後現在局面上可以獲得的是k
轉移的就是前面的相反數的最大值(表示我走完這一步可以得到的錢數)上一步是對手走的我不管
TAT最後的結果就是終態,遍歷i可走的地方,j可以走的地方
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define SIZE_D 55
#define SIZE_B 2605
#define INF 0x3f3f3f3f
using namespace std;
int digit[SIZE_D];
//const int INF = 0x3f3f3f3f;
int X,Y;
struct pp
{
    int to,next;
}pic[SIZE_B];
int e,head[SIZE_D];
void init()
{
    e = 0;
    memset(head,-1,sizeof(head));
}
void addedge(int x,int y)
{
    pic[e].to = y;
    pic[e].next = head[x];
    head[x] = e++;
}

const int alter = 1300;
int dp[SIZE_D][SIZE_D][SIZE_B];
int finalres,sum;
int dfs(int white,int black,int all)
{
    if (dp[white][black][alter+all]!= -INF){
        return dp[white][black][alter+all];
    }
    int res = -INF;
    for (int i = head[white]; i != -1; i = pic[i].next){
        int v = pic[i].to;
        dfs(v,black,all + digit[v]);
        if (res  <= -dp[v][black][alter+all + digit[v]]){
            res = -dp[v][black][alter+all + digit[v]];
            if (white == X && black == Y){
                if (finalres < res){
                    finalres = res;
                    sum = 1;
                }else if (finalres == res){
                    sum++;
                }
            }
        }
        res = max(res,-dp[v][black][alter+all + digit[v]]);
    }
    //printf("1st    res = %d\n",res);
    for (int i = head[black]; i != -1; i = pic[i].next){
        int v = pic[i].to;
        dfs(white,v,all - digit[v]);
        if (res <= -dp[white][v][alter+all- digit[v]]){
            res = (-dp[white][v][alter+all- digit[v]]);
            if (white == X && black == Y){
                if (finalres < res){
                    finalres = res;
                    sum = 1;
                }else if (finalres == res){
                    sum++;
                }
            }
        }
    }
    if (res == -INF){
        res = -all;
        if (white == X && black == Y)
            sum = 1;
    }
    return dp[white][black][alter+all] = res;
}
int main()
{
    int N,M;
    while (~scanf("%d %d %d %d",&N,&M,&X,&Y)){
        for (int i = 0; i < N; i++)
            scanf("%d",&digit[i]);
        init();
        for (int i = 0; i < M; i++){
            int tempx,tempy;
            scanf("%d %d",&tempx,&tempy);
            addedge(tempx,tempy);
        }
        for (int i = 0; i <= N; i++)
            for (int j = 0; j <= N; j++)
                for (int k = 0; k < SIZE_B-1; k++)
                    dp[i][j][k] = -INF;
        sum = 0;
        finalres = -INF;
        dfs(X,Y,1);
        printf("%d %d\n",finalres,sum);
    }
    return 0;

}

G題:有個妹子每天住在一個地方,她可以獲得一定的收益,每一天得到的錢不同,然後她可以搬到指定的地方numi,那個地方的人類可以幫她搬,但是這個人類搬到某個指定的地方,妹子就要請這個人喫飯,花費指定的錢數bgmi,問妹子在d天晚上之前可以得到的最大錢數。

題解:dp[i][j][k] .i代表這是第i天下午,j代表今晚準備住在j號房子,k代表今晚住完了之後,累計在j住了k天。注意,一定能夠要清楚dp中每一維代表的是今晚的情況還是昨晚的情況,很容易糾結不清。

然後這題有個巨大的問題。就是妹子一個下午可以搬家很多很多次啊!天啊,要跑一遍弗洛伊德得出每兩個點的最小花費啊!注意正常的弗洛伊德,自己到自己的花費是0,但這道題自己到自己的花費要初始化成INF喲!然後和其他點等同,假如跑了一圈回到自己,那麼今晚就相當於第1天而不是第k+1天。



弗洛伊德模板!注意不同問題有不同的初始化方法!!!

int d[SIZE_N][SIZE_N];
void warshall_floyd(int V)
{
    for (int k = 0; k < V; k++)
        for (int i = 0; i < V; i++)
            for (int j = 0; j < V; j++)
                d[i][j] = min(d[i][j],d[i][k] + d[k][j]);
}

這道題的ac代碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define SIZE_N 330
#define INF 0x3f3f3f3f
using namespace std;
int a[SIZE_N][5],b[SIZE_N],c[SIZE_N],dp[SIZE_N][SIZE_N][5];
int m[SIZE_N][SIZE_N][5];
int mnum[SIZE_N];

int d[SIZE_N][SIZE_N];
void warshall_floyd(int V)
{
    for (int k = 0; k < V; k++)
        for (int i = 0; i < V; i++)
            for (int j = 0; j < V; j++)
                d[i][j] = min(d[i][j],d[i][k] + d[k][j]);
}


int main()
{
    //freopen("input.txt","r",stdin);
    int n,day;
    while (~scanf("%d %d",&n,&day)){
        memset(d,INF,sizeof(d));
        for (int i = 0; i < n; i++){
            scanf("%d %d %d",&a[i][1],&a[i][2],&a[i][3]);
            a[i][4] = a[i][3];
            //int m;
            scanf("%d",&mnum[i]);
            for (int j = 0; j < mnum[i]; j++){
                scanf("%d %d",&m[i][j][0],&m[i][j][1]);
                d[i][m[i][j][0]] = m[i][j][1];
            }
        }
        memset(dp,-1,sizeof(dp));

        warshall_floyd(n);


        //dp[0][0][0] = 0;
        dp[1][0][1] = 0;//a[0];//下午還是0的
        for (int i = 1; i <= day; i++)
            for (int j = 0; j < n; j++)
                for (int k = 1; k <= 4; k++){

                    if (dp[i][j][k] == -1) continue;
                    //printf("\n\n i = %d j = %d k = %d\n",i,j,k);
                    int now = dp[i][j][k] + a[j][k];//當前收益
                    for (int l = 0; l < n; l++){
                        if (now < d[j][l]) continue;
                        dp[i+1][l][1] = max(now - d[j][l], dp[i+1][l][1]);
                    }
                    if (k <= 3){
                        dp[i+1][j][k+1] = max(now,dp[i+1][j][k+1]);
                    }else{
                        dp[i+1][j][4] = max(now,dp[i+1][j][4]);
                    }
                }
        int sum = 0;
        for (int j = 0; j < n; j++)
            for (int k = 1; k <= 4; k++)
                sum = max(sum, dp[day][j][k]);
        printf("%d\n",sum);
    }
    return 0;
}

J題:模擬題。

注意的地方:“namely shifting amnesty for yellow cards from the end of the first round until after the quarter-finals instead”

首先 四分之一決賽指的是倒數第三場……好吧……決賽<——半決賽<——四分之一決賽……

然後,這句話的 意思是指進入第五場時,前三場的黃牌就可以自動消失,第四場的黃牌保留。

還有,題目中也沒說給的比賽號碼是按順序的。

以上。但我根本沒做這道題,反正我模擬題的姿勢渣的一比。


B題:有四種反轉方案,只要你選擇一種能經過最少次這種翻轉可以得到全白的界面,如果有多種選擇序號最小。

題解:首先弄一個可愛的矩陣,三行的。枚舉每個方塊出現的位置,也就是k從0到1<<15。只要k中哪個位置有1,就代表這個位置轉一次。然後,注意有很多都是右側一列最中間只有一個1的,把他們集體逆時針90度,然後只需要遍歷第一行就知道放還是不放啦。

存矩陣的時候把每個角度都轉一遍存下來~一共存四個矩陣,然後每個矩陣一行的十五個數字壓縮成二進制表示一個數字。然後每種方法放的時候遍歷第一行就好啦。

存方法數矩陣的時候有一個特別厲害的姿勢可以存下k從0到1<<15中所有的矩陣。

所有的都逆時針九十度了所以記得原矩陣也要逆時針90度。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章