E. Tournament 1260E(dp,贪心)

You are organizing a boxing tournament, where boxers will participate ( is a power of 2), and your friend is one of them. All boxers have different strength from 1 to , and boxer wins in the match against boxer if and only if is stronger than .

The tournament will be organized as follows: boxers will be divided into pairs; the loser in each pair leaves the tournament, and 2 winners advance to the next stage, where they are divided into pairs again, and the winners in all pairs advance to the next stage, and so on, until only one boxer remains (who is declared the winner).

Your friend really wants to win the tournament, but he may be not the strongest boxer. To help your friend win the tournament, you may bribe his opponents: if your friend is fighting with a boxer you have bribed, your friend wins even if his strength is lower.

Furthermore, during each stage you distribute the boxers into pairs as you wish.

The boxer with strength can be bribed if you pay him dollars. What is the minimum number of dollars you have to spend to make your friend win the tournament, provided that you arrange the boxers into pairs during each stage as you wish?

Input
The first line contains one integer (2≤≤218) — the number of boxers. is a power of 2.

The second line contains integers 1, 2, …, , where is the number of dollars you have to pay if you want to bribe the boxer with strength . Exactly one of is equal to −1 — it means that the boxer with strength is your friend. All other values are in the range [1,109].

Output
Print one integer — the minimum number of dollars you have to pay so your friend wins.

Examples
inputCopy
4
3 9 1 -1
outputCopy
0
inputCopy
8
11 -1 13 19 24 7 17 5
outputCopy
12
Note
In the first test case no matter how you will distribute boxers into pairs, your friend is the strongest boxer and anyway wins the tournament.

In the second test case you can distribute boxers as follows (your friend is number 2):

1:2,8:5,7:3,6:4 (boxers 2,8,7 and 6 advance to the next stage);

2:6,8:7 (boxers 2 and 8 advance to the next stage, you have to bribe the boxer with strength 6);

2:8 (you have to bribe the boxer with strength 8);

题意: n个人比赛。每次两两比。力气大的获胜剩下n/2个人继续比。最后只剩一个人就是获胜者。你有个朋友在里面,你可以贿赂对方,这样你的朋友在那个轮次一定赢。问最少花多少钱使你朋友胜出。
思路: 佳爷说是背包dp然后秒了。
但是二进制的思路貌似更好理解。假设8个人,你的朋友力量值排第4.那么你朋友肯定在前半部分胜出,后半部分留下一个人,你只要贿赂后半部分那个人就可以了。这时候就需要优先队列,记录下最小的值加上。

具体阐述的话,假设这个人编号为x。那么递归的考虑,前半部分通过贿赂或者正常比赛剩下了x。后半部分剩下了最大的n(x ≠ n),这个必须的贿赂,花费是n。而x如果大于n / 2,那么前半部分不需要考虑。否则要递归到前半部分用同样的思路考虑,此时n/2是不可战胜的(x≠n/2),然后同样的思路递归下去,直到递归到底层。

因为这个递归的决策性是单向最优的,所以你直接for过去不用记录状态,也就是俗称贪心也可以。递归的算法我是从官方题解那里膜的QAQ。

#include 
#include 
#include 
#include 

using namespace std;

const int maxn = (1 << 18) + 5;
int a[maxn],vis[maxn];

struct Node{
    int x;
    bool operator<(const Node &rhs)const
    {
        return x > rhs.x;
    }
};

priority_queue<Node>q;

int main()
{
    int n;scanf("%d",&n);
    for(int i = 1;i <= n;i++)
    {
        scanf("%d",&a[i]);
    }
    int x = 1;
    while(x <= n)
    {
        vis[x] = 1;
        x <<= 1;
    }

    long long ans = 0;
    for(int i = n;i >= 1;i--)
    {
        if(a[i] == -1)
            break;
        q.push(Node{a[i]});
        if(vis[i])
        {
            ans += q.top().x;
            q.pop();
        }
    }
    printf("%lld\n",ans);
    return 0;
}

ACNEW,更新了递推代码

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6;
int n;

ll dp[maxn][25],a[maxn],sum[maxn];

ll DP(ll cnt,ll pos) {// 已经打败了pos-1个人,之前已经贿赂了cnt个人,还需要花多少钱。
    ll &res = dp[cnt][pos];
    if(res != -1) return res;
    if(a[pos] == -1) return res = 0;
    
    res = INF;
    ll rem = sum[cnt] - pos;
    if(cnt < 20) res = DP(cnt + 1,pos + 1) + a[pos];
    if(rem >= 0) res = min(res,DP(cnt,pos + 1));
    return res;
}

ll DP2() { //打败i个人,贿赂了j个人的最小花费。
    ll ans = 0;
    memset(dp,0x3f,sizeof(dp));
    dp[0][0] = 0;
    for(int i = 1;i <= n;i++) {
        for(int j = 1;j <= 20 && j <= i;j++) {
            if(sum[j] - i < 0) {
                continue;
            }
            dp[i][j] = min(dp[i - 1][j - 1] + a[i],dp[i - 1][j]);
            if(a[i + 1] == -1) {
                return dp[i][j];
            }
        }
    }
    return 0;
}

int main() {
    scanf("%d",&n);
    for(int i = 1;i <= n;i++) {
        scanf("%lld",&a[i]);
    }
    reverse(a + 1,a + 1 + n);
    int x = n / 2;
    for(int i = 1;i <= 20;i++,x >>= 1) {
        sum[i] = sum[i - 1] + x; //sum代表贿赂了i个人,可以共打败sum[i]个人
    }
    printf("%lld\n",DP2());
//    memset(dp,-1,sizeof(dp));
//    printf("%lld\n",DP(0,1));
    return 0;
}

记忆化代码

#include 
#include 
#include 

using namespace std;

typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = (1 << 18) + 5;
ll dp[25][maxn],a[maxn],sum[1005];

ll solve(int cnt,int pos)//贿赂cnt个人,打败pos个人
{
    ll &res = dp[cnt][pos];
    if(res != -1)return res;
    
    if(a[pos] == -1)return res = 0;
    
    res = INF;
    ll rem = sum[cnt] - pos;
    if(cnt < 20)res = solve(cnt + 1,pos + 1) + a[pos];
    if(rem > 0) res = min(res,solve(cnt,pos + 1));
    return res;
}

int main()
{
    int n;scanf("%d",&n);
    for(int i = 0;i < n;i++)
    {
        scanf("%lld",&a[i]);
    }
    reverse(a,a + n);
    int x = n / 2;
    for(int i = 1;i <= 20;i++,x >>= 1)
    {
        sum[i] = sum[i - 1] + x;
    }
    printf("\n");
    memset(dp,-1,sizeof(dp));
    printf("%lld\n",solve(0,0));
    return 0;
}

你可能感兴趣的:(#,二进制,#,codeforces,#,线性dp)