CS R17 C(思维DP) D(二进制,淘汰) E(好题:博弈+DP)

题意:给出排列a,操作:将任意一个数放到开头或者结尾.
n<=1e5,问将序列变为从小到的顺序的最小操作次数,并输出方案.

显然每个元素只要移动一次,若同一个元素有多次移动,则用其最后一次移动 造成的效果是相同的.
每个元素要么不移动要么移动一次, 求 最少需要操作元素 -> 求最多不需要操作的元素有多少个?
假如不需要移动的元素集合S 则S显然为连续的一段数,否则需要移动.

设dp[a[i]]为以a[i]结尾的最长的一段连续序列,dp[a[i]]=dp[a[i]-1]+1. a[i]-1在a[i]之后则dp[a[i]为1.

找到元素个数最多的S之后,mx=max(S),mn=min(S) 则只要把mx+1,mx+2..n移动最后,把mn-1,mn-2...1.移动到前面即可得到方案.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;
const int N=2e5+20,inf=0x3f3f3f3f;
int n,a[N],dp[N];
int main()
{
	int res=0,l,r;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		dp[a[i]]=dp[a[i]-1]+1;
		if(res<dp[a[i]])
			res=dp[a[i]],r=a[i];
	}
	l=r-res+1;
	cout<<n-res<<endl;
	for(int i=l-1;i>=1;i--)
		printf("%d 0\n",i);
	for(int i=r+1;i<=n;i++)
		printf("%d 1\n",i);
	return 0;
}

Problem D
题意:给出n个数,问选k个数,and运算最大能为多少?
n,k<=1e5,a[i]<=1e9.

高位能为1尽量为1,bit位从高到低,采用淘汰法.
若bit位为1的>=k个 则这些数为待选  其余的数肯定不能选 淘汰掉即可.
若待选元素中bit位为1的<k个,则该位只能为0.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;
const int N=2e5+20,inf=0x3f3f3f3f;
int n,k,a[N],vis[N];
int main()
{
	ll ans=0;
	cin>>n>>k;
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=31;i>=0;i--)
	{
		int cnt=0;
		for(int j=1;j<=n;j++)
		{
			if(vis[j])	continue;
			if((a[j]>>i)&1)
				cnt++;	
		}
		if(cnt>=k)
		{
			ans+=(1ll<<i);
			for(int j=1;j<=n;j++)
			{
				if((((a[j]>>i)&1))==0)
					vis[j]=1;
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

Problem E
题意:n点m条边的有向无环图(DAG),图中总共有k点能量.
给出初始k个能量的分布位置(同一个结点上可以有多个能量)
现在A,B两人轮流操作,每轮将所有的能量移动到其能到达下一个结点(若有多个结点选择,选最优的).
n,m,k<=1e5,当某人不能操作时算输,问A先手是否必胜?


先考虑简单的情况,只有一个石子,为每个结点定义状态n/p 表示石头在这个结点开始时,先手是必败还是必胜?
所以当某个结点出去结点都为必胜时,该结点为必败.
存在出的的某个结点为必败时 该结点为必胜.


现在有多个石子.若此时所有石子为所在结点都为必胜或者必败 游戏就可以结束了
但是存在有些为必胜 有些为必败  此时应该如何解决?
Alice此时先手 对于状态为必胜的石子 Alice总是有机会移动,对于状态为必败的石子,Alice会先于BoB无法移动的.BOB对必败必胜的石子也是如此.
则最优的策略就是 Alice尽早结束它必胜的石子,也就是让BoB尽早出现无法移动的局面,然后尽量延迟必败石子的移动次数 让自己活的更久.


dp[i]为i开始移动石头的次数,对于结点i
若i为必胜态,则先手的要让它尽快结束,移动到dp[v]最小的一个必败态上.
若i为必败态,则要拖延时间 移动到dp[v]最大的一个v上即可.


总共的轮数取决于:所有初始有石头结点开始移动的次数的最小值.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;
const int N=2e5+20,inf=0x3f3f3f3f;
int n,c[N],dp[N],ans;
vector<int> e[N];
int f(int u)
{
	if(dp[u]!=-1)
		return dp[u];
	int res=0;
	for(int i=0;i<e[u].size();i++)
	{
		int v=e[u][i];
		f(v);
		if(dp[v]%2)// go v then u is p
		{
			if(res%2==0)//u is p
				res=max(res,dp[v]+1);
		}
		else
		{
			if(res%2==0)// u is actually n
				res=dp[v]+1;
			else
				res=min(res,dp[v]+1);
		}
	}
	return dp[u]=res;
}
int main()
{
	int n,m,k;
	memset(dp,-1,sizeof(dp));
	cin>>n>>m>>k;
	for(int i=1;i<=k;i++)
		scanf("%d",&c[i]);
	int u,v,ans=inf;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		e[u].push_back(v);
	}
	for(int i=1;i<=k;i++)
		ans=min(ans,f(c[i]));
	puts(ans%2?"A":"B");
	return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值