2022“杭电杯”中国大学生算法设计超级联赛(10)(3,7,9)

本文讲述了在涂黑游戏和波形数组问题中,通过策略优化降低操作次数和最大化得分的方法。涂黑游戏中,玩家遵循规则避免相邻黑色格子;波形数组则涉及奇偶位置元素调整。博主揭示了解决思路和代码优化过程,展示了从博弈论到具体算法的转化。
摘要由CSDN通过智能技术生成

第3题

给出一个长为n的数组,你每次操作可以将其中的一个数字加一或减一,求最少操作次数让该数组成为一个波形数组。波形数组的判定为,数组中除了第一个和最后一个外的元素a[i],都满足两边的元素同时大于a[i]或小于a[i]。

难度不大,先确定是奇数位为大数还是偶数位为大数。接着从左往右贪心,对每个i=2,3,4,n ,如果 ai与ai-1的大小关系不符合条件,则调整 。最后取个min即可。但我们最开始太着急了,题目没有认真去思考,导致t了好几发。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int b[N],a[N],c[N];
int n;
void solve(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&b[i]);
		a[i]=b[i];
		c[i]=b[i];
	}
	long long ans=0;
	for(int i=2;i<=n;i++){
		if(i%2==0){
			//cout<<a[4]<<" "<<a[3]<<endl;
			if(a[i]<=a[i-1]) ans+=a[i-1]-a[i]+1,a[i]+=a[i-1]-a[i]+1;
		}
		else {
				if(a[i]>=a[i-1]) ans+=a[i]-a[i-1]+1,a[i]-=a[i]-a[i-1]+1;
			}
	}
	long long res=0;
	for(int i=2;i<=n;i++){
		if(i%2==0){
			if(c[i]>=c[i-1]) res+=c[i]-c[i-1]+1,c[i]-=c[i]-c[i-1]+1;
		}
		else{
			if(c[i]<=c[i-1]) res+=c[i-1]-c[i]+1,c[i]+=c[i-1]-c[i]+1;
		}
	}
	//cout<<res<<" "<<ans<<endl;
	printf("%lld\n",min(res,ans));
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--){
		solve();
	}
}

第7题

你会得到一棵无向树n节点。保证n是均匀的。您将删除一些边缘(至少1),并且必须让每个其余连接的组件具有偶数个顶点。计算删除满足此类约束的边的方法数,模数998244353.

很容易产生思路,dfs一遍树,找出每个子树下面的节点个数(包括自己本身),如果为偶数的话,这个地方就可以切一刀,然后找出所有的子树节点个数,可以找出最多切几刀,然后再排列组合一下就可以了。

不过不知道为什么我写的一直T,耽误了好长时间,但队友根据我的思路,写了差不多的代码就AC了,好奇怪。

#include<bits/stdc++.h>
using namespace std;
int read() {
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
int ans[100005],f[100005],head[500005],num;
struct op
{
  int next,to;
};
op g[2000000];

void add(int from,int to)
{  
  g[num].next=head[from];
  g[num].to=to;
  head[from]=num++;
}

int dfs(int x)
{
    for (int i=head[x]; i!=-1; i=g[i].next)
  {
    int e=g[i].to; 
    if(!f[e]){
            f[e]=1;
            ans[x]=ans[x]+dfs(e);
            f[e]=0;
        }
  }
    ans[x]++;
    return ans[x];
}
int main()
{
    int n,q;
    long long cnt,sum;
    q=read(); 
    while (q--){
    	memset(head,-1,sizeof(head));
    	n=read();
    	sum==0;
    	for(int i=1; i<=n-1; i++){
        	int x,y;
        	x=read();
        	y=read();
        	add(x,y);
    		add(y,x);
   		}
    	f[1]=1;
    	dfs(1);
   		if(n%2!=0)
        	printf("0\n");
    	else{
    		cnt=0;
        	for(int i=1;i<=n;i++){
            	if(ans[i]%2==0)
                	cnt=cnt+1;
    		}
    		long long ppp=0;
          cnt=cnt%998244353;
    		for (int i=1;i<=cnt-1;i++){
				long long kt=1;
    			for (int j=cnt-1;j>=cnt-i;j--)
    				kt=(kt*j)%998244353;
    			for (int j=1;j<=i;j++)
    				kt=(kt/j)%998244353;
				ppp=(ppp+kt)%998244353;
			}
			printf("%d\n",ppp);
		}	
    }
    return 0;
}

队友的代码,感觉差不多。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const long long mod=998244353;
typedef long long ll;
ll f[N],nf[N];
int h[N],e[2*N],ne[2*N],idx,siz[N];
void add(int a,int b){
	e[++idx]=b;
	ne[idx]=h[a];
	h[a]=idx;
}
ll qmi(ll a,ll b){
	ll res = 1;
    while (b)
    {
        if (b & 1) res = (ll)res * a % mod;
        a = (ll)a * a % mod;
        b >>= 1;
    }
    return res;
}
int n;
void dfs(int u,int fa){
	siz[u]=1;
	for(int i=h[u];i!=-1;i=ne[i]){
		int j=e[i];
		if(j==fa) continue;
		dfs(j,u);
		siz[u]+=siz[j];
	}
}
void solve(){
	scanf("%d",&n);
	memset(h,-1,sizeof h);
	idx=0;
	memset(siz,0,sizeof siz);
	for(int i=1;i<n;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		add(a,b),add(b,a);
	}
	//cout<<'@';
	//memset(dp,0,sizeof dp);
	dfs(1,0);
	int res=0;
	for(int i=2;i<=n;i++){
		if(siz[i]%2==0)  res++;
	}
	ll ans=0;
	for(int i=1;i<=res;i++){
		ans=(ans+f[res]*nf[i]%mod*nf[res-i]%mod)%mod;
	}
	//cout<<dp[2][1][1]<<endl;
	cout<<ans%mod<<endl;
}
int main()
{
	f[0]=1,nf[0]=1;
	for(int i=1;i<=N-10;i++){
		f[i]=f[i-1]*i%mod;
		nf[i]=nf[i-1]%mod*qmi(i,mod-2)%mod;
	}
	int t;
	scanf("%d",&t);
	while(t--){
		solve();
	}
}

第9题

有一个纸条分为n空白网格。对于每个i( i < n)(1≤i<n)网 格i和i+1被视为相邻的。爱丽丝和鲍勃将在大道上玩一个游戏。他们轮流移动。在一个动作中,玩家必须将剩余的空白网格之一涂成黑色,同时保留没有两个黑色网格相邻的规则。当其中一个玩家无法绘制任何网格时,游戏结束,游戏的分数定义为涂成黑色的网格总数。爱丽丝想把分数降到最低,而鲍勃想把分数最大化。鉴于n和开始游戏的一方,找出两个玩家发挥最佳状态时的最终得分。

很明显是博弈论,找规律,但我们可能还是不熟悉,写的代码全部都WA了。看了一下大佬的思路。

我们把涂黑操作想象成剪掉连续的三个格子。如果每个连续段长度都<=2 ,那么结果就确定了。
在此之前,可以发现,Alice 的一种最优策略是:选某个连续段的左数第二个格子涂黑
Bob 的一种最优策略是:选某个连续段的左数第三个格子涂黑。设f(n) , g(n) 分别为 Alice、Bob 面对长度为n的空纸带时的答案。则有f(n) = g(n-3)+1, g(n) = f(n-3)+2,注意到f(n) = f(n-7)+3, g(n) = g(n-7)+3,因此可以快速算出答案。对于<=7内的边界情况可以枚举以后特判一下。

原来这么简单,但我们当时确实没想到,一直在找答案的规律。

#include<bits/stdc++.h>
using namespace std;

int main(){
    int T;  cin>>T;
    while(T--){
        int n;  string op;  cin>>n>>op;
        int ans = n/7*3;
        if(op[0]=='A'){
            if(n%7>=1) ans++;
            if(n%7>=4) ans++;
            if(n%7>=6) ans++;
        }else{
            if(n%7>=1) ans++;
            if(n%7>=3) ans++;
            if(n%7>=5) ans++;
        }
        cout<<ans<<"\n";
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值