JZOJ3404[NOIP2013模拟]卡牌游戏(2019.08.04[NOIP提高组]模拟 B 组T2)

传送门

卡牌游戏 (Standard IO)

Time Limits: 1000 ms  Memory Limits: 524288 KB  Detailed Limits  

Time to Submit: 01:59:17

Description

小X 为了展示自己高超的游戏技巧,在某一天兴致勃勃地找小Y 玩起了一种卡牌游戏。每张卡牌有类型(攻击或防御)和力量值两个信息。

小Y 有n 张卡牌,小X 有m 张卡牌。已知小X 的卡牌全是攻击型的。

游戏的每一轮都由小X 进行操作,首先从自己手上选择一张没有使用过的卡牌X。如果小Y 手上没有卡牌,受到的伤害为X 的力量值,否则小X 要从小Y 的手上选择一张卡牌Y。若Y 是攻击型(当X 的力量值不小于Y 的力量值时才可选择),此轮结束后Y 消失,小Y 受到的伤害为X 的力量值与Y 的力量值的差;若Y 是防御型(当X 的力量值大于Y 的力量值时才可选择),此轮结束后Y 消失,小Y 不受到伤害。

小X 可以随时结束自己的操作(卡牌不一定要用完)。希望聪明的你帮助他进行操作,使得小Y 受到的总伤害最大。

Input

输入的第一行包含两个整数n 和m 。

接下来n 行每行包含一个字符串和一个整数,分别表示小Y 的一张卡牌的类型(“ATK”表示攻击型,“DEF”表示防御型)和力量值。

接下来m 行每行包含一个整数,表示小X 的一张卡牌的力量值。

Output

输出一行包含一个整数,表示小Y 受到的最大总伤害。

Sample Input

输入1:

2 3

ATK 2000

DEF 1700

2500

2500

2500

输入2:

3 4

ATK 10

ATK 100

ATK 1000

1

11

101

1001

Sample Output

输出1:

3000

【样例说明1】

第一轮,小X 选择自己的第一张卡牌和小Y 的第二张卡牌,小Y 的第二张卡牌消失。

第二轮,小X 选择自己的第二张卡牌和小Y 的第一张卡牌,小Y 的第一张卡牌消失,同时受到500 点伤害。

第三轮,小X 选择自己的第三张卡牌,此时小Y 手上已经没有卡牌,受到2500 点伤害。

小X 结束游戏,小Y 共受到3000点伤害。

输出2:

992

【样例说明2】

第一轮,小X 选择自己的第三张卡牌和小Y 的第一张卡牌,小Y 的第一张卡牌消失,同时受到91点伤害。

第二轮,小X 选择自己的第四张卡牌和小Y 的第二张卡牌,小Y 的第二张卡牌消失,同时受到901点伤害。

小X 结束游戏,小Y 共受到992点伤害。

 

Data Constraint

各规模均有一半数据满足小Y 只有攻击型卡牌。

对于30%的数据,1≤ n,m ≤ 6。

对于60%的数据,1≤ n,m ≤10^3。

对于100%的数据,1≤ n,m ≤10^5,力量值均为不超过10^6的非负整数。

 一、暴力搜索(30)

这个题目前能保证正确的貌似是这个搜索,然而它是会TLE的。

#include<bits/stdc++.h>
using namespace std;
inline int poread()
{
    char c = 0;
    int res = 0;
    while(!isdigit(c = getchar()));
    do
    {
        res = res * 10 + c - 48;
    }
    while(isdigit(c = getchar()));
    return res;
}
const int MAXN = 1e5 + 5;
int n,m;
struct node
{
    bool isstack;
    int force;
}Y[MAXN];
int X[MAXN];
bool v_Y[MAXN],v_X[MAXN];
int ans;
void dfs(int now,int cnt)
{
    ans = max(ans,now);
//    cerr<<cnt<<":"<<now<<" "<<x<<" "<<y<<endl;
//    for(register int i = 1; i <= n; ++i)
//        cerr<<v_Y[i];
//    cerr<<endl;
//    for(register int i = 1; i <= m; ++i)
//        cerr<<v_X[i];
//    cerr<<endl;
    if(cnt > m )
    {
        return;
    }
    int sum = now;
    if(cnt >= n)
    {
        for(register int j = 1; j <= m; ++j)
        {
            if(!v_X[j])
            {
                
                sum = now;
                v_X[j] = true;
                sum += X[j];
                dfs(sum, cnt + 1);
                v_X[j] = false;
            }
            
        }
    }
    else
    {
        for(register int i = 1; i <= n; ++i) 
        {
            if(!v_Y[i])
            {
                for(register int j = 1; j <= m; ++j) 
                {
                    
                    if(!v_X[j])
                    {
                        sum = now;
                        
                        if(Y[i].isstack)
                        {
                            if(X[j] < Y[i].force)
                                continue;
                            sum += (X[j] - Y[i].force);
                        }
                        else
                        {
                            if(X[j] <= Y[i].force)
                                continue;
                        }
                        v_Y[i] = true, v_X[j] = true;
                        dfs(sum,cnt + 1);
                        v_Y[i] = false,    v_X[j] = false;
                    }
                }
            }    
        }    
    }        
}
int main()
{
#ifdef lky233
    freopen("testdata.in","r",stdin);
    freopen("testdata.out","w",stdout);
#endif
    n = poread();
    m = poread();
    for(register int i = 1; i <= n; ++i)
    {
        char s[10];
        scanf("%s",s);
        Y[i].force = poread();
        Y[i].isstack = (s[0] == 'A');
    }
    for(register int i = 1; i <= m; ++i)
    {
        X[i] = poread();
    }
    dfs(0,0);
    cout<<ans<<endl;
}

二、出锅的贪心

这个贪心策略是这样的,首先对于这个问题,有两种策略:

  1、直接攻击对方的攻击牌,拿到分数后结束游戏。

    师曰:对于这个策略,我们将x从大到小排序,y从小到大排序,一直攻击直到结束。

    对于样例2,该策略可以通过,而且原本是A了这个题的。

    Hack:  

    2 3
    ATK 599
    ATK 1000
    1200
    600
    450

    显然如果使用上述策略,ans = 601,但是1200->1000,600->599,450->脸,会获得651的分数。

  2、拿光对手的所有牌,并用自己的牌打对方脸直接攻击。

    对于这个策略,应该用最小的消耗解除掉对答案没有产生贡献的盾牌,之后使用策略1攻击攻击牌,最后打脸。

    对于出锅的数据,采用了把y从大到小排一遍进行计算然后取max的方式过掉了。(面向数据编程大法)

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int poread()
{
    char c = 0;
    int res = 0;
    while(!isdigit(c = getchar()));
    do
    {
        res = res * 10 + c - 48;
    }
    while(isdigit(c = getchar()));
    return res;
}
const int MAXN = 1e5 + 7;
int n,m;
int ans_1,ans_2,ans_3;
struct node
{
    int x;
    bool isstack;
    void in()
    {
        char s[10];
        scanf("%s",s);
        isstack = (s[0] == 'A');
        x = poread();
    }
}datax[MAXN],datay[MAXN],x[MAXN],y[MAXN],tmpdata[MAXN];
bool cmp1(node a,node b)//从小到大 
{
    return a.x < b.x;
}
bool cmp2(node a,node b)//从大到小
{
    return a.x > b.x;
}
void tan_1()//只打掉Attack 
{
    int tmpans_1 = 0,tmpans_2 = 0;
    int tmpm = 0,tmpn = 0;
    memset(x,0,sizeof(x));
    memset(y,0,sizeof(y));
    memcpy(x,datax,sizeof(datax));
    for(register int i = 1;i <= n; ++i)
    {
        if(datay[i].isstack)
            y[++tmpn] = datay[i];
    }
    sort(y+1,y+tmpn+1,cmp1);
    sort(x+1,x+m+1,cmp2);
    int nn = min(tmpn,m);
    for(register int i = 1; i <= nn; ++i)
    {
        if(x[i].x < y[i].x)
            break;
        tmpans_1 += x[i].x - y[i].x;
    }
    sort(y+1,y+tmpn+1,cmp2);
    for(register int i = 1; i <= nn; ++i)
    {
        if(x[i].x < y[i].x)
            break;
        tmpans_2 += x[i].x - y[i].x;
    }
    ans_1 = max(tmpans_1,tmpans_2);
}
bool v[MAXN];
void tan_2()
{
    if(n > m)
    {
        ans_2 = 0;
        return;
    }
    int tmpans_1 = 0,tmpans_2 = 0;
    memset(x,0,sizeof(x));
    memset(y,0,sizeof(y));
    for(register int i = 1; i <= m; ++i)
    {
        tmpdata[i] = datax[i];
    }
    int tmpm = 0,tmpn = 0;
    for(register int i = 1; i <= n; ++i)
    {
        if(!datay[i].isstack)
            y[++tmpn] = datay[i];
    }
    //双指针,破盾 
    sort(tmpdata+1,tmpdata+m+1,cmp1);
    sort(y+1,y+tmpn+1,cmp1);
    int point1 = 1,point2 = 1,tmp_cnt = 0;

    while(point1 <= m && point2 <= tmpn)
    {
//        cerr<<point1<<endl;
        while(tmpdata[point1].x <= y[point2].x)
        {
            if(point1 > m)
                break;
            ++point1;
        }
        if(tmpdata[point1].x > y[point2].x)
            ++tmp_cnt,v[point1] = true;
        ++point2;
        ++point1;
    }
//    cerr<<1<<endl;
    //不能打破所有的盾,这个策略不可行。 
    if(tmp_cnt < tmpn)
    {
        ans_3 = 0;
        return;
    }
    tmpn = tmpm = 0;
    for(register int i = 1; i <= m; ++i)
    {
        if(!v[i])
            x[++tmpm] = tmpdata[i];    
    }
    memset(y,0,sizeof(y));
    for(register int i = 1; i <= n; ++i)
    {
        if(datay[i].isstack)
            y[++tmpn] = datay[i];
    }
    
    
    sort(y+1,y+tmpn+1,cmp1);
    sort(x+1,x+tmpm+1,cmp2);
    int nn = min(tmpn,tmpm),mm = max(tmpn,tmpm);
    #ifdef lky233
    for(register int i = 1; i <= tmpm; ++i)
        cerr << x[i].x << endl;
    for(register int i = 1; i <= tmpn; ++i)
        cerr << y[i].x << endl;
    #endif
    bool con = 1;
    for(register int i = 1; i <= nn; ++i)
    {
        if(x[i].x < y[i].x)
        {
            con = 0;
            break;
        }
        tmpans_1 += x[i].x - y[i].x;
    }
    if(con)
        for(register int i = nn + 1; i <= mm; ++i)
        {
            tmpans_1 += x[i].x;
        }
    con = 1;
    sort(y+1,y+tmpn+1,cmp2);
    for(register int i = 1; i <= nn; ++i)
    {
        if(x[i].x < y[i].x)
        {
            con = 0;
            break;
        }
        tmpans_2 += x[i].x - y[i].x;
    }
    if(con)
        for(register int i = nn + 1; i <= mm; ++i)
        {
            tmpans_2 += x[i].x;
        }
    ans_2 = max(tmpans_1,tmpans_2);
//    cerr<<tmpans_1<<' '<<tmpans_2<<endl;
    return;
}
signed main()
{
#ifdef lky233
    freopen("testdata.in","r",stdin);
    freopen("testdata.out","w",stdout);
#endif
    n = poread();
    m = poread();
    for(register int i = 1; i <= n; ++i)
        datay[i].in();
    for(register int i = 1; i <= m; ++i)
        datax[i].x = poread();
    tan_1();
    tan_2();
//    cerr<<ans_1<<" "<<ans_2<<endl;
    cout<<max(ans_1,ans_2)<<endl;
    return 0;
}

冗余代码极多还能优化……

三、意识到贪心是正确的然后来补充

Hack数据实际可认为是策略2。

策略2修正:破掉对方的盾牌,之后己方sigma Attack - 对方 sigma Attack;

策略1证明: 使用一个更小的数攻击对方的数,会减小贡献值。

策略2: 只要可以破掉对方盾牌,攻击顺序其实已经不会影响最终答案了……

代码咕咕咕掉,回宿舍了。

猜你喜欢

转载自www.cnblogs.com/Shiina-Rikka/p/11300035.html