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; }
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; }