hdu 4924 Football Manager(搜索+剪枝+dp)

4 篇文章 0 订阅

题意:n个队员,每个队员能担任几个位置中的一个,队员有一个当前能力和潜在能力,现在要组一个给定阵型人的足球队,要求队员的当前能力和最大,如果有多种,要求潜在能力尽可能大。

思路:其实就是很暴力的做法,先选出11个人,在求这11个人组成足球队的最大价值。求这个的方法可以用dp,dp[i][j][k][l]表示前i个人,有了j个DF,k个MF,l个GK,ST可以用i-j-k-l算出来,这样dp下来就可以算出这11个人能组合出的最大值。不过这样写还是非常慢的,复杂度也非常高,还需要剪枝,将队员按他拥有的最大CA值从大到小排序,每次找出11人时,算出可能获得的最大价值,看是否大于当前答案,另外,还要判断一下,选出的11人理想情况下能否组成完整阵容。官方题解还加了个优化,把最大的值放到ST上,加了以后感觉也没快多少,100ms左右吧。。。

吐槽:这题真是神坑,不仅足球队有自恋的人,还有讨厌自己的人,那么讨厌自己怎么不去屎啊,魂淡(怒)。。。


代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=25;
const int mod=100000;
struct Player
{
    int id,flag[4],CA[4],PA[4],Cmax;
    void clear()
    {
        id=0;
        memset(flag,0,sizeof(flag));
    }
}player[maxn];
int g[110][110],lineup[4],n,m,ans1,ans2;
char str[55];
bool cmp(Player a, Player b)
{
    return a.Cmax > b.Cmax;
}
int getpos()
{
    if(str[0] == 'D') return 0;
    if(str[0] == 'M') return 1;
    if(str[0] == 'S') return 2;
    return 3;
}
int getsign()
{
    if(str[0] == 'L') return 1;
    return -1;
}
void SWAP(int x,int y)
{
    for(int i = 0; i < n;++i)
    {
        swap(player[i].flag[x],player[i].flag[y]);
        swap(player[i].CA[x],player[i].CA[y]);
        swap(player[i].PA[x],player[i].PA[y]);
    }
    swap(lineup[x],lineup[y]);
}
void getlineup()
{
    scanf("%d-%d-%d",&lineup[0],&lineup[1],&lineup[2]);
    if(lineup[0] > lineup[1]) SWAP(0,1);
    if(lineup[1] > lineup[2]) SWAP(1,2);
}
int select[maxn],dp[15][11][11][2],dp2[15][11][11][2];
bool isFind;
inline void Update(int i,int a,int b,int c,int ca,int pa)
{
    if(ca > dp[i][a][b][c])
    {
        dp[i][a][b][c] = ca;
        dp2[i][a][b][c] = pa;
    }
    else if(ca == dp[i][a][b][c] && pa > dp2[i][a][b][c])
        dp2[i][a][b][c] = pa;
}
void cal()
{
    int a = 0,b = 0;
    int cnt[4] = {0};
    for(int i = 0;i < 11;++i)
    {
        for(int j = i;j < 11;++j)
        {
            int u = player[select[i]].id;
            int v = player[select[j]].id;
            a += g[u][v];
            if(i != j) a += g[v][u];
        }
        b += player[select[i]].Cmax;
        for(int j = 0 ;j < 4 ;++j)
            if(player[select[i]].flag[j]) cnt[j]++;
    }
    for(int i =0;i < 4 ;++i)
        if(cnt[i] < lineup[i]) return ;
    if(a + b < ans1) return ;
    b = 0;
    for(int i = 0;i <= 11;++i)
        for(int j = 0;j <= lineup[0];++j)
            for(int k = 0;k <= lineup[1] ;++k)
                for(int l =0 ;l < 2;++l)
                    dp[i][j][k][l] = dp2[i][j][k][l]= -inf;
    dp[0][0][0][0] = 0;
    dp2[0][0][0][0] = 0;
    int ca , pa;
    for(int i = 0;i < 11;++i)
    {
        int t = select[i];
        for(int DF = 0;DF <= lineup[0];++DF)
            for(int MF = 0;MF <= lineup[1];++MF)
                for(int GK = 0;GK < 2; ++GK)
                {
                    if(dp[i][DF][MF][GK] == -inf) continue;
                    int ST=i-GK-DF-MF;
                    ca = dp[i][DF][MF][GK];
                    pa = dp2[i][DF][MF][GK];
                    if(player[t].flag[3] && GK < 1)
                        Update(i+1,DF,MF,GK+1,ca +player[t].CA[3],pa +player[t].PA[3]);
                    if(player[t].flag[0] && DF < lineup[0])
                        Update(i+1,DF+1,MF,GK,ca+player[t].CA[0],pa+player[t].PA[0]);
                    if(player[t].flag[1] && MF < lineup[1])
                        Update(i+1,DF,MF+1,GK,ca+player[t].CA[1],pa+player[t].PA[1]);
                    if(player[t].flag[2] && ST < lineup[2])
                        Update(i+1,DF,MF,GK,ca+player[t].CA[2],pa+player[t].PA[2]);
                }
    }
    if(dp[11][lineup[0]][lineup[1]][1] == -inf) return ;
    a += dp[11][lineup[0]][lineup[1]][1];
    b += dp2[11][lineup[0]][lineup[1]][1];
    if(a > ans1)
    {
        isFind = true;
        ans1 = a;
        ans2 = b;
    }
    else if(a == ans1)
        ans2=max(ans2,b);
}
void dfs(int pos, int now)
{
    if(now == 11)
    {
        cal();
        return ;
    }
    if(pos >= n) return ;
    select[now] = pos;
    dfs(pos + 1,now +1);
    if(n - pos + now < 11) return ;
    dfs(pos + 1,now);
}
int main()
{
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w ",stdout);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        memset(g,0,sizeof(g));
        int num,pos;
        for(int i = 0;i < n ;++i)
        {
            player[i].clear();
            scanf("%d",&player[i].id);
            scanf("%d",&num);
            player[i].Cmax = -inf;
            for(int j = 0;j < num ;++j)
            {
                scanf("%s",str);
                pos = getpos();
                player[i].flag[pos]=1;
                scanf("%d%d",&player[i].CA[pos],&player[i].PA[pos]);
                player[i].Cmax=max(player[i].Cmax,player[i].CA[pos]);
            }
        }
        sort(player , player + n ,cmp);
        scanf("%d",&m);
        int u,v,w;
        for(int i = 0;i < m;++i)
        {
            scanf("%d%d",&u,&v);
            scanf("%s", str);
            scanf("%d",&w);
            g[u][v] = getsign() * w;
        }
        getlineup();
        ans1 = ans2 = -inf;
        isFind = false;
        dfs(0,0);
        if(isFind) printf("%d %d\n",ans1,ans2);
        else printf("Poor Manager!\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值