Codeforces Round #608 (Div. 2)

D题:https://codeforces.com/contest/1271/problem/D

题意:你初始有k只士兵,n个城堡,你要求要逐一攻破。

   给出的信息a[i],b[i],c[i]代表第i个城堡被攻破需要a[i]个士兵(攻破不会造成士兵死亡),在第i个城堡你可以招募b[i]士兵,守护第i个城堡你能得到c[i]的价值。

   守护城堡有俩种方法:1、在攻破 i 城堡后留下一个士兵

             2、通过往回输送士兵守护;

   然后往回输送题目有给输送的m条路径;  

   问能攻破所有城堡情况下的最大价值

分析:要求全部攻破,所以我们应该在最后攻破的时候再来进行分配的问题才是正确的;

   因为我们如果要比较早就在 i 城堡设置士兵,也可以在最后的时候分配

   dp[i][j]表示当前在第 i号堡垒,带有第 jj个士兵能获得的最大价值。

   俩个转移:1、///留士兵

        2、///传说士兵回去

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int inf=0x3f3f3f3f;
const int M=5e3+3;
int a[M],b[M],c[M],dp[M][M],f[M];
vector<int>val;
int main(){
    int n,m,k;
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
        cin>>a[i]>>b[i]>>c[i];
    for(int i=1;i<=n;i++)
        f[i]=i;
    while(m--){
        int u,v;
        cin>>u>>v;
        f[v]=max(f[v],u);///从最后传送过来
    }
    for(int i=0;i<=n+1;i++){
        for(int j=0;j<M;j++)
            dp[i][j]=-inf;
    }
    dp[1][k]=0;
    for(int i=1;i<=n;i++){
        val.clear();
        for(int j=1;j<=i;j++)
            if(f[j]==i)
                val.pb(c[j]);
        sort(val.begin(),val.end());
        reverse(val.begin(),val.end());
        for(int j=0;j<M;j++){///留士兵
            if(j>=a[i]&&dp[i][j]>=0)
                dp[i+1][j+b[i]]=max(dp[i][j],dp[i+1][j+b[i]]);
        }
        for(auto x:val){///传说士兵回去
            for(int j=0;j<M;j++)
                if(dp[i+1][j]>=0)
                    dp[i+1][j-1]=max(dp[i+1][j-1],dp[i+1][j]+x);
        }
    }
    int ans=-1;
    for(int i=0;i<M;i++)
        ans=max(ans,dp[n+1][i]);
    cout<<ans;
    return 0;
}
View Code

 

E题:http://codeforces.com/contest/1271/problem/E

分析:分别考虑偶数和奇数,对于每一个x,反推回去的话,可得[2*x,2*x+1]之间的数都是可到到达的,且x越小能到达的值越多,所以满足单调性,二分解决

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,k;
bool check(ll x){
    ll l=x,r=x;
    if(x%2==0)
        r++;
    ll ans=0;
    while(true){
        ans+=min(n,r)-l+1;
        l<<=1;
        r<<=1;
        r++;
        if(l>n)
            break;
    }
    return ans>=k;
}
int main(){
    cin>>n>>k;
    ll l=1,r=(n+1)/2;
    ll ans=0;
    while(l<=r){///二分枚举合法奇数
        ll midd=(l+r)>>1;
        if(check(2*midd-1))
            l=midd+1,ans=2*midd-1;
        else
            r=midd-1;
    }
    l=1,r=n/2;
    while(l<=r){///偶数
        ll midd=(l+r)>>1;
        if(check(2*midd))
            l=midd+1,ans=max(2*midd,ans);
        else
            r=midd-1;
    }
    cout<<ans<<endl;
    return 0;
}
View Code
posted @ 2019-12-18 15:37  starve_to_death  阅读(245)  评论(0编辑  收藏  举报