【题解】ABC 209

E.

题意:给定一个初始字符串,可以把它接龙下去,最后无法操作的人判负。若双方都采用最优策略,即先保证自己不输,再击败对方,输出博弈结果。 n<=2e5

Solution:

这是一道图论的博弈问题,考虑回溯 + 逆推。

首先这个平局很难判断,我们不妨先求出必胜或必败的状态,如果两者都不是,则说明是平局状态。首先初度为零是必败态,其次,如果后继有一个必败态,则它为必胜态;如果后继全为必胜态,则它为必败态。

这个环比较难搞,不妨看下图:

在这里插入图片描述

这是环上都有值的情况。有时候会形成一个封闭的环,双方都不愿意走出这个环,否则都是自己输,如下图:
在这里插入图片描述

那么此时环上的状态都是平局了。

最后考虑优化建边,不难想到将首和尾连边,这样出发点就是尾字符。

时间复杂度 O(n)

#include<bits/stdc++.h>
#define ins insert
#define mp make_pair
#define pii pair<int,int>
#define pb push_back
#define vec vector
using namespace std;
const int mx=2e5+5;
const int mxn=52*52*52;
int n,m,dp[mxn],deg[mxn];
string s;
vec<int> son[mx];
pii edge[mx];
queue<int> Q;
int char_to_int(char c) {
	if('A'<=c&&c<='Z') return c-'A';
	else return c-'a'+26;
}
int string_to_int(string s) {
	for(auto &c:s) {
		c=char_to_int(c);
	}
	return s[0]*52*52+s[1]*52+s[2];
}
//思路:回溯+逆推 
int main() {
//	freopen("data.in","r",stdin);
	memset(dp,-1,sizeof(dp));
    ios_base::sync_with_stdio(false);	
    cin>>n;
        for(int i=1;i<=n;i++) {
    	cin>>s;
    	int x=string_to_int(s.substr(0,3)),y=string_to_int(s.substr(s.size()-3,3));
//    	cout<<x<<" "<<y<<endl;
    	edge[i]=mp(x,y);
    	son[y].pb(x),deg[x]++;
// 		appear[id[i]]=1;
	}
	int Lose=0,Win=1;
	for(int i=0;i<mxn;i++) {
		if(!deg[i]) {
			dp[i]=Lose;
			Q.push(i);
		}
	}
	while(Q.size()) {
		int x=Q.front(); Q.pop();
		if(dp[x]==Win) {
			for(auto y:son[x]) {
				if(~dp[y]) continue;
				if(--deg[y]==0) {
					dp[y]=Lose;
					Q.push(y);
				}
			}
		}
		else {
			for(auto y:son[x]) {
				if(~dp[y]) continue;
				dp[y]=Win;
				Q.push(y);
			}
		}
	}
	for(int i=1;i<=n;i++) {
		if(dp[edge[i].second]==Lose) cout<<"Takahashi"<<endl;
		else if(dp[edge[i].second]==Win) cout<<"Aoki"<<endl;
		else cout<<"Draw"<<endl;
	}
}

F.

题意:给定 n 个数以及高度,砍掉一棵树的代价为 h[i-1]+h[i]+h[i+1] ,同时令 h[i]=0 。求一个砍树的排列,使得总花费最少。求排列的个数。

Solution:

考虑每一棵树 i,如果它相邻的树比它先砍,则自己每次要增加代价 h[i]

这样,对于 1<=i<=n-1 ,如果 h[i]<h[i+1] ,则 i+1 先砍最优;如果 h[i]>h[i+1] ,则 i 先砍最优。

那么令 dp[i][j]i 棵树的所有排列数,使得树 i 是要砍掉的第 j 个元素 ,这里有隐含条件 j<=i 。不难写出转移式,通过前缀和优化,时间复杂度 O(n^2)

这种为每个转换插入元素的 DP 结构称为“插入 DP”。

#include<bits/stdc++.h>
#define ll long long
#define ins insert
#define mp make_pair
#define pii pair<int,int>
#define pb push_back
#define vec vector
using namespace std;
const int mx=4005;
const int mod=1e9+7;
ll dp[mx][mx],res;
int n,a[mx];
//dp[i][j] 前 i 棵树的所有排列数,使得树 i 是要砍掉的第 j 个元素 
int main() {
//	freopen("data.in","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
	}
	dp[1][1]=1;
	for(int i=2;i<=n;i++) {
		for(int j=1;j<=i;j++) {
			if(a[i-1]<a[i]) { 
				dp[i][j]=(dp[i][j]+dp[i-1][i-1]-dp[i-1][j-1])%mod;
			}	
			else if(a[i-1]>a[i]) {
				dp[i][j]=(dp[i][j]+dp[i-1][j-1])%mod;
			}
			else {
				dp[i][j]=(dp[i][j]+dp[i-1][i-1])%mod;
			}
			dp[i][j]=(dp[i][j]+dp[i][j-1])%mod;
//			printf("dp[%d][%d]=%d\n",i,j,dp[i][j]);
		}
	}
	res=dp[n][n];
	if(res<0) res+=mod;
	printf("%lld",res);
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值