Codeforces Round #608 Div2

1271D Portals(DP 贪心)

题意:

​ 在一个战略游戏中,你需要率领士兵占领n个城堡,游戏开始时你将率领k名士兵,而城堡必须按顺序被占领且占领每个城堡后将有新的士兵加入军队(无士兵损失),然后你将抉择是否对城堡进行守卫。有两种方式:1. 留下一名士兵在当前所在的城堡中进行守卫;2. 共有m个通道,每个通道连接着两个城堡,假设有一个通道连接城堡u和v,若当前在城堡u中,你可以通过管道将一名士兵送到城堡v进行守卫。每个城堡有三个权值a、b、c,分别代表占领该城堡所需的士兵数、占领该城堡后新加入的士兵数、守卫该城堡所得的分数。求占领所有城堡后,守卫城堡所得分数的最大值 ( 1 n 5000 k + b 5000 ) (1\le n\le5000,k+\sum b \le5000)

解法:

​ 一个重要的贪心策略:对任意城堡而言,能晚守卫就尽量晚守卫。因为在占领城堡的过程中除非守卫城堡,士兵的数量是不会减少的,由于通道的存在,我们可以在士兵足够多时再去守卫之前占领过的城堡,因此维护每个城堡的最迟守卫时间

​ 又因为城堡的占领是按顺序进行,考虑DP: d p [ i ] [ j ] dp[i][j] 表示占领前i个城堡后率领j名士兵时守卫城堡所得的分数,转移方程如下:

d p [ i ] [ j + b [ i ] ] = d p [ i 1 ] [ j ] dp[i][j+b[i]]=dp[i-1][j] a [ i ] j 5000 b [ i ] a[i]\le j\le 5000-b[i]

d p [ i ] [ j 1 ] = m a x ( d p [ i ] [ j 1 ] , d p [ i ] [ j ] + c [ k ] ) dp[i][j-1]=max(dp[i][j-1],dp[i][j]+c[k]) 1 j 5000 1\le j\le 5000 且城堡k的最迟守卫时间为i

​ 复杂度: O ( 5000 n ) O(5000*n)

#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(二分 数学)

题意:

​ 定义 f ( x ) = { x 2 , x   i s   e v e n x 1 , x   i s   o d d f(x)=\begin{cases} \frac x2,x\ is\ even\\x-1,x\ is \ odd\end{cases} ,显然对任意整数x,有 f ( f ( . . . f ( x ) ) ) = 1 f(f(...f(x)))=1 ,将集合 { x , f ( x ) , f ( f ( x ) ) , . . . , 1 } \{x,f(x),f(f(x)),...,1\} 记为 p a t h ( x ) path(x) 。给定n、k,对所有1到n之间的数x写出 p a t h ( x ) path(x) ,求最大的数y,使y在所有的 p a t h ( x ) path(x) 中出现至少k次 ( 1 k n 1 0 18 ) (1\le k\le n\le10^{18})

解法:

​ 考虑 f ( y ) f(y) 的反函数,分奇偶两种情况讨论:若y为奇数,则当x=y,2y,2y+1,4y,4y+1,4y+2,4y+3,…时,y将出现​在 p a t h ( x ) path(x) 中;若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将出现在 p a t h ( x ) path(x) 中。因此,若已知y,我们可以在 l o g n logn 时间内求出y在所有 p a t h ( x ) path(x) 中出现的次数。

​ 另外,对奇偶性相同的两数显然答案具有单调性,即 c n t ( x ) c n t ( x + 2 ) cnt(x)\ge cnt(x+2) ,其中 c n t ( y ) cnt(y) 表示y在所有 p a t h ( x ) path(x) 中出现的次数。故考虑二分答案,分别对奇数和偶数两种情况进行二分即可。

​ 复杂度: O ( l o g ( l o g n ) ) O(log(logn))

#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(枚举)

题意:

​ 将一群学生分为两个小组,学生只有数学、编程、体育三门课程,每门课以小组为对象且有一定容量,即上课人数不能过多。在第一个小组中三门课程的容量分别为 a 1 b 1 c 1 a_1、b_1、c_1 ,第二个小组容量分别为 a 2 b 2 c 2 a_2、b_2、c_2 。在所有学生中:有 d 1 d_1 名学生三门课都上,有 d 2 d_2 名只上数学和编程课,有 d 3 d_3 名只上数学和体育课,有 d 4 d_4 名只上数学课,有 d 5 d_5 名只上编程和体育课,有 d 6 d_6 名只上编程课,有 d 7 d_7 名只上体育课。求能够满足两个小组所有课程容量的分组方案 ( 1 a , b , c 3000 ) (1\le a,b,c\le 3000)

解法:

​ 枚举3种只上两门课的学生,设两个小组课程的剩余容量分别为 a x 1 b x 1 c x 1 ax_1、bx_1、cx_1 a x 2 b x 2 c x 2 ax_2、bx_2、cx_2 ,则三门课都上的学生数量不能超过 m i n ( a x 1 , b x 1 , c x 1 ) + m i n ( a x 2 b x 2 c x 2 ) min(ax_1,bx_1,cx_1)+min(ax_2、bx_2、cx_2) ,从课容量中去掉这些学生后,再在剩余容量中考虑三种只上一门课的学生即可。

​ 复杂度: O ( n 3 ) O(n^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");
	}
}
发布了5 篇原创文章 · 获赞 3 · 访问量 123

猜你喜欢

转载自blog.csdn.net/weixin_43899905/article/details/104099012
今日推荐