hdu 5005 Compromise (2014 ACMICPC regional Anshan Site 1009)


題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=5005


題目巨長,所以不貼題目了,直接說大意(話說大意也不短。。。。)。

題目大意:有兩個人A和X,給一個有向無環圖(DAG),每一個出度爲0的節點(下面稱這些節點爲“葉子節點”)有兩個權值x和y(所有的x,y都不一樣,這點非常重要)。除了葉子節點,其他所有節點都都由A或X控制。如果當前位置在非葉子節點U,如果U被A控制,則A可以選擇接下來轉移的點V(滿足U->V存在一條有向邊),反之亦然。如果到達了葉子節點,則A得x分,X得y分。現在A和X玩一個遊戲,從1節點出發,每個人都希望自己的得分最大(重點)。現在X有一種特殊的操作,他可以告訴A他的策略,所謂策略就是在每個X所控制的點,X會選擇的轉移節點。(X一定會按照策略行動,至少A是這麼認爲的)。現在問兩個問題:

1.X不使用特殊操作時,所能得到的最大分數。

2.X使用特殊操作時,所能得到的最大分數。


思路:第一問無比簡單,因爲是DAG,設dp[i][0],dp[i][1]分別表示在i節點A和X所能得到的最大得分。然後分葉子節點,A控制的節點,X控制節點三種情況進行轉移即可,應該都會。

關鍵是第二問,乍一看來好像告訴對方自己的策略是一種愚蠢的行爲,並不會對最後的得分有多少改進,但是考慮到雙方的目標都是使得自己的得分最大,而不是要使對方的得分最小或是自己和對方的差值最大(這點很重要)。所以X可以採用一種策略使得自己的得分比不使用特殊操作更大,具體看樣例即可。至於怎麼制定自己的策略,直接制定貌似很難(我不會而已),我們可以換一種思路,因爲A,X最後的得分一定是一個葉子節點的權值,那麼我們可以按照y值從大到小依次枚舉,枚舉該葉子節點是否可以由B的某種策略到達,如果到達了顯然答案就是當且枚舉的葉子節點的y權值了(因爲是從大到小枚舉)。

現在考慮一個葉子節點,設爲V。V是否可以到達不僅取決於X,還取決於A的心情,X當然想要到達V,因爲V是目前爲止的最好選擇,前面比V好的都無法到達(否則也不會枚舉V了。。。),現在問題就是A是否會選擇也到達V。設V的x權值爲Limit,那麼如果A可以到達更好的葉子節點使得其權值大於Limit,A纔不會選擇V呢,那麼現在的關鍵問題是要保證A無法到達比V還優(對A來說)的葉子節點。我們來定義一個節點是“好的”當且僅當從這個節點出發,無論A怎麼轉移,X都能使用策略使得A無法到達一個比V更優的葉子節點。否則這個節點稱爲“壞的”。那麼我們可以根據dp來算出每個節點是“好的”還是“壞的”。

對於葉子節點,如果其x值大於Limit,則它是"壞的",否則它是"好的"。

對於非葉子節點,(1)如果它被A控制,且它的後繼節點有一個是壞的,則說明A可以到達一個比V更優的節點,說明它是”壞的“。否則A無論如何也只能到達”好的"點,則它是“好的”。

(2)如果它被X控制,且它的後繼節點中有一個是“好的”,則說明X可以選擇轉移到“好的”節點中去,所以它是“好的”,否則它是“壞的”。

那麼如果1號節點是"壞的",表示A可以選擇一個比V更優的葉子節點,則不能到達V。如果1號節點是“好的”節點,也只能說明X可以用策略使得A無法到達一個比V更優的葉子節點,並不能說明可以到達V。

V能到達的依據爲:如果存在一條僅由“好的”點組成的簡單路徑從1號節點到V,那麼說明V可達。

那麼這個時候只要從1號節點dfs一遍即可判斷。

現在說明一下爲什麼這樣能到達V。因爲1號節點是“好的”,說明對於A來說,V是對他來說最優的葉子節點,(其他更優節點已被標爲“壞的”,而X是不會讓A走到“壞的”節點上去的),同時對X來說,V也是最優節點(前面說過了),那麼現在又有一條從1號節點到V的僅由好的節點組成的路徑。那麼V可達。那麼這道題就做完了。

最後說一下X的策略,由上面的分析,可知,如果V可達,X只要告訴A:“對於我所有控制的點,我必然會轉移到一個好的後繼節點(至於是哪個根據V的選擇而定)。即可,這樣的話,A只能選擇V節點爲最優節點了。終於寫完了。。。。。

代碼如下:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <vector>
#define maxn 210
using namespace std;
struct node{
    int id,A,B;
    node(){}
    node(int x,int y,int z){
        id=x,A=y,B=z;
    }
};
vector<int> e[maxn];
vector<node> vec;
int bel[maxn],A[maxn],B[maxn],dp[maxn][2],vis[maxn];;
bool cmp(const node &x,const node &y){
    return x.B>y.B;
}
void init(int n){
    for(int i=1;i<=n;i++)
    e[i].clear();
}
void dfs1(int now){//cannot make the claim.
    if(dp[now][0]!=-1)//記憶化搜索
    return;
    if(bel[now]==-1){//不屬於任何人
        dp[now][0]=A[now];
        dp[now][1]=B[now];
        return;
    }
    for(int i=0;i<e[now].size();i++){
        int v=e[now][i];
        dfs1(v);
        if(bel[now]==0){//屬於A
            if(dp[v][0]>dp[now][0]){
                dp[now][0]=dp[v][0];
                dp[now][1]=dp[v][1];
            }
        }
        else{//屬於X
            if(dp[v][1]>dp[now][1]){
                dp[now][0]=dp[v][0];
                dp[now][1]=dp[v][1];
            }
        }
    }
}
int dfs2(int now,int limit){//can make the claim
    if(vis[now]!=-1)
    return vis[now];
    if(bel[now]==-1){
        if(A[now]>limit)
        return vis[now]=0;
        return vis[now]=1;
    }
    for(int i=0;i<e[now].size();i++){
        int v=e[now][i];
        dfs2(v,limit);
    }
    for(int i=0;i<e[now].size();i++){
        int v=e[now][i];
        if(bel[now]==0){
            if(!vis[v])
            return vis[now]=0;
        }
        else if(vis[v])
        return vis[now]=1;
    }
    if(bel[now]==0)
    return vis[now]=1;
    return vis[now]=0;
}
int dfs3(int now,int limit){
    if(bel[now]==-1){
        return limit==A[now];
    }
    for(int i=0;i<e[now].size();i++){
        int v=e[now][i];
        if(vis[v]==1&&dfs3(v,limit))
        return 1;
    }
    return 0;
}
int main()
{
   // freopen("dd.txt","r",stdin);
    int ncase,n,num,x;
    char typ[4];
    scanf("%d",&ncase);
    while(ncase--){
        scanf("%d",&n);
        init(n);
        vec.clear();
        memset(bel,-1,sizeof(bel));
        memset(dp,-1,sizeof(dp));
        for(int i=1;i<=n;i++){
            scanf("%d",&num);
            if(num==0){//葉子節點
                scanf("%d%d",&A[i],&B[i]);
                vec.push_back(node(i,A[i],B[i]));
            }
            else{
                for(int j=0;j<num;j++){
                    scanf("%d",&x);
                    e[i].push_back(x);
                }
                scanf("%s",typ);
                if(typ[0]=='A')
                bel[i]=0;
                else
                bel[i]=1;
            }
        }
        dfs1(1);
        int ans1=dp[1][1];
        sort(vec.begin(),vec.end(),cmp);
        int ans2=-1;
        for(int i=0;i<(int)vec.size();i++){//枚舉X到達的葉子節點
            memset(vis,-1,sizeof(vis));
            int now=vec[i].id;
            if(dp[now][0]==-1)//若now不能由根節點到達,則跳過
            continue;
            if(dfs2(1,vec[i].A)){
                if(dfs3(1,vec[i].A)){
                    ans2=vec[i].B;
                    break;
                }
            }
        }
        printf("%d %d\n",ans1,ans2);
    }
    return 0;
}



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