1271D Portals(DP 贪心)
题意:
在一个战略游戏中,你需要率领士兵占领n个城堡,游戏开始时你将率领k名士兵,而城堡必须按顺序被占领且占领每个城堡后将有新的士兵加入军队(无士兵损失),然后你将抉择是否对城堡进行守卫。有两种方式:1. 留下一名士兵在当前所在的城堡中进行守卫;2. 共有m个通道,每个通道连接着两个城堡,假设有一个通道连接城堡u和v,若当前在城堡u中,你可以通过管道将一名士兵送到城堡v进行守卫。每个城堡有三个权值a、b、c,分别代表占领该城堡所需的士兵数、占领该城堡后新加入的士兵数、守卫该城堡所得的分数。求占领所有城堡后,守卫城堡所得分数的最大值 。
解法:
一个重要的贪心策略:对任意城堡而言,能晚守卫就尽量晚守卫。因为在占领城堡的过程中除非守卫城堡,士兵的数量是不会减少的,由于通道的存在,我们可以在士兵足够多时再去守卫之前占领过的城堡,因此维护每个城堡的最迟守卫时间。
又因为城堡的占领是按顺序进行,考虑DP: 表示占领前i个城堡后率领j名士兵时守卫城堡所得的分数,转移方程如下:
,
, 且城堡k的最迟守卫时间为i
复杂度: 。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e3+5;
int a[maxn],b[maxn],c[maxn],idx[maxn],dp[maxn][maxn];
vector<int>p[maxn];
int main()
{
int n,m,tot,u,v;
cin>>n>>m>>tot;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i]>>c[i],idx[i]=i;
for(int i=1;i<=m;i++)
cin>>u>>v,idx[v]=max(idx[v],u);
for(int i=1;i<=n;i++)
p[idx[i]].push_back(i);
memset(dp,-1,sizeof(dp));
dp[0][tot]=0;
for(int i=1;i<=n;i++)
{
for(int j=a[i];j+b[i]<maxn;j++)
dp[i][j+b[i]]=dp[i-1][j];
for(int j:p[i]) for(int k=1;k<maxn;k++)
if(dp[i][k]!=-1)
dp[i][k-1]=max(dp[i][k-1],dp[i][k]+c[j]);
}
int ans=-1;
for(int i=0;i<maxn;i++)
ans=max(ans,dp[n][i]);
cout<<ans;
}
1271E Common Number(二分 数学)
题意:
定义 ,显然对任意整数x,有 ,将集合 记为 。给定n、k,对所有1到n之间的数x写出 ,求最大的数y,使y在所有的 中出现至少k次 。
解法:
考虑 的反函数,分奇偶两种情况讨论:若y为奇数,则当x=y,2y,2y+1,4y,4y+1,4y+2,4y+3,…时,y将出现在 中;若y为偶数,则当x=y,y+1,2y,2y+1,2y+2,2y+3,4y,4y+1,4y+2,4y+3,4y+4,4y+5,4y+6,4y+7,…时,y将出现在 中。因此,若已知y,我们可以在 时间内求出y在所有 中出现的次数。
另外,对奇偶性相同的两数显然答案具有单调性,即 ,其中 表示y在所有 中出现的次数。故考虑二分答案,分别对奇数和偶数两种情况进行二分即可。
复杂度: 。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,k,ans;
bool check(ll mval)
{
ll cnt=0,x=1;
if((mval&1)==0) mval>>=1,cnt--;
while(x*(mval+1)<n)
cnt+=x,x<<=1;
cnt+=max(0LL,n-x*mval+1);
return cnt>=k;
}
int main()
{
cin>>n>>k;
for(int i=0;i<=1;i++)
{
ll l=0,r=n>>1;
while(l<=r)
{
ll mid=(l+r)>>1;
if(check(mid<<1|i)) l=mid+1;
else r=mid-1;
}
ans=max(ans,r<<1|i);
}
cout<<ans;
}
1271F Divide The Students(枚举)
题意:
将一群学生分为两个小组,学生只有数学、编程、体育三门课程,每门课以小组为对象且有一定容量,即上课人数不能过多。在第一个小组中三门课程的容量分别为 ,第二个小组容量分别为 。在所有学生中:有 名学生三门课都上,有 名只上数学和编程课,有 名只上数学和体育课,有 名只上数学课,有 名只上编程和体育课,有 名只上编程课,有 名只上体育课。求能够满足两个小组所有课程容量的分组方案 。
解法:
枚举3种只上两门课的学生,设两个小组课程的剩余容量分别为 , ,则三门课都上的学生数量不能超过 ,从课容量中去掉这些学生后,再在剩余容量中考虑三种只上一门课的学生即可。
复杂度: 。
#include<bits/stdc++.h>
using namespace std;
int t,a[4][3],d[8],f[8];
bool valid(int x,int y,int z)
{
int a11=a[1][1]-y-z,a12=a[1][2]-d[3]-d[2]+y+z;
int a21=a[2][1]-x-z,a22=a[2][2]-d[5]-d[2]+x+z;
int a31=a[3][1]-x-y,a32=a[3][2]-d[5]-d[3]+x+y;
if(a11<0||a12<0||a21<0||a22<0||a31<0||a32<0) return false;
int f1=min({a11,a21,a31});
int f2=min({a12,a22,a32});
if(d[1]>f1+f2) return false;
int ans1=min(f1,d[1]);
int ans4=min(d[4],a11-ans1);
if(a12<d[1]+d[4]-ans1-ans4) return false;
int ans6=min(d[6],a21-ans1);
if(a22<d[1]+d[6]-ans1-ans6) return false;
int ans7=min(d[7],a31-ans1);
if(a32<d[1]+d[7]-ans1-ans7) return false;
cout<<ans1<<" "<<z<<" "<<y<<" "<<ans4<<" "<<x<<" "<<ans6<<" "<<ans7<<endl;
return true;
}
int main()
{
cin>>t;
while(t--)
{
for(int i=1;i<=2;i++)
for(int j=1;j<=3;j++)
cin>>a[j][i];
for(int i=1;i<=7;i++) cin>>d[i];
bool ok=false,tag=true;
if(d[1]+d[2]+d[3]+d[4]>a[1][1]+a[1][2]) tag=false;
if(d[1]+d[2]+d[5]+d[6]>a[2][1]+a[2][2]) tag=false;
if(d[1]+d[3]+d[5]+d[7]>a[3][1]+a[3][2]) tag=false;
for(int i=0;i<=d[5]&&tag;i++)
for(int j=0;j<=d[3]&&tag;j++)
for(int k=0;k<=d[2]&&tag;k++)
if(valid(i,j,k)) ok=true,tag=false;
if(!ok) puts("-1");
}
}