2023年广东工业大学腾讯杯新生程序设计竞赛

E.不知道叫什么名字

题意:找一段连续的区间,使得区间和为0且区间长度最大,输出区间长度。
思路:考虑前缀和,然后使用map去记录每个前缀和第一次出现的位置,然后对数组进行扫描即可。原理:若 s [ i ] = x s[i]=x s[i]=x,则, m a p [ x ] = i map[x]=i map[x]=i,当后续出现 s [ j ] = x s[j]=x s[j]=x时, s [ j ] − s [ i ] = 0 s[j]-s[i]=0 s[j]s[i]=0,则合法的区间长度为 j − i j-i ji
将此题一般化为,找一段连续的区间,使得区间和为 k 且长度最大,输出区间长度,我们同样使用这个方法,去记录前缀和,然后对数组进行扫码即可,对于前缀和 s [ j ] s[j] s[j]而言,因为 s [ j ] − ( s [ j ] − k ) = k s[j]-(s[j]-k)=k s[j](s[j]k)=k,所以需要定位到 s [ j ] − k s[j]-k s[j]k第一次出现的位置即可。

#include 

using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
int mod = 1e9+7;
// const int maxv = 4e6 + 5;
// #define endl "\n"



void solve()
{
	int n;
	cin>>n;
	vector<int> a(n+5),s(n+5);
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++){
		s[i]=s[i-1]+((a[i]==1)?1:-1);
	}
	map<int,int> mp;
    mp[0]=0;
	for(int i=1;i<=n;i++){
		if(!mp.count(s[i])) mp[s[i]]=i;
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		int x=s[i];
		ans=max(ans,i-mp[x]);
	}
	cout<<ans<<endl;


}

int main()
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
// 	cin>>t;
	while(t--){
		solve();
	}
   	system("pause");
    return 0;
}

G.闪闪发光心动不已!

题意:找到包含kira…doki的最长子序列。
思路:运用前后缀的思想,对字符串正向扫描一遍,然后逆向扫描一遍,对于位置i而言,其最大长度为其前面的kira子序列+其后面doki的子序列。

#include 

using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> ar;
int mod = 1e9+7;
// const int maxv = 4e6 + 5;
#define endl "\n"

void solve()
{
    int n;
    cin>>n;
    string s;
    cin>>s;
    s=" "+s;
    vector<int> pre(n+5),suf(n+5);
    int tot=0;
    for(int i=1;i<=n;i++){
        pre[i]=pre[i-1];
        if(tot==0&&s[i]=='k') tot++;
        else if(tot==1&&s[i]=='i') tot++;
        else if(tot==2&&s[i]=='r') tot++;
        else if(tot==3&&s[i]=='a') tot++;
        if(tot==4) pre[i]+=4,tot=0;
    }
//     for(int i=1;i<=n;i++) cout<
    tot=0;
    for(int i=n;i>=1;i--){
        suf[i]=suf[i+1];
        if(tot==0&&s[i]=='i') tot++;
        else if(tot==1&&s[i]=='k') tot++;
        else if(tot==2&&s[i]=='o') tot++;
        else if(tot==3&&s[i]=='d') tot++;
        if(tot==4) suf[i]+=4,tot=0;
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        if(pre[i]&&suf[i+1]) ans=max(ans,pre[i]+suf[i+1]);
    }
    cout<<ans<<endl;
}

int main()
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
// 	cin>>t;
	while(t--){
		solve();
	}
   	system("pause");
    return 0;
}

I.uu爱玩飞行棋

题意:距离终点m米,然后给定n个操作,每次可以走x步,若不能直接到达终点则需返回多余的步数,问走到终点的最小操作。
思路:考虑背包,设计状态 d p [ i ] [ j ] dp[i][j] dp[i][j],表示对于前 i i i个操作,还剩 j j j步的最小操作数量。

#include 

using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
int mod = 1e9+7;
// const int maxv = 4e6 + 5;
// #define endl "\n"

int dp[2010][2010];

void solve()
{
	int n,m;
	cin>>n>>m;
	vector<int> a(m+5);
	for(int i=1;i<=m;i++){
		cin>>a[i];
	}
	memset(dp,0x3f,sizeof dp);
	dp[0][n]=0;
	for(int i=1;i<=m;i++){
		for(int j=0;j<=n+5;j++){
                dp[i][j]=min(dp[i-1][j],dp[i-1][j+a[i]]+1);//当前状态由j+a[i]转移而来
                if(j<a[i])dp[i][j]=min(dp[i-1][j],dp[i-1][a[i]-j]+1);//表示反弹的状态
            }
	}
	if(dp[m][0]>1e9/2) cout<<-1<<endl;
	else cout<<dp[m][0]<<endl;


}

int main()
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
// 	cin>>t;
	while(t--){
		solve();
	}
   	system("pause");
    return 0;
}

J.火柴人小游戏

2023年广东工业大学腾讯杯新生程序设计竞赛_第1张图片
思路:很容易想到二分,需要思考如何进行check,我们同样容易发现,我们可以贪心去选择战斗力较小的且能到达的点,所以我们无论我们初始的战斗力如何,我们的战斗顺序都是固定的,显然,普通的队列并不能满足我们的贪心需求,我们使用优先队列去生成我们的路径即可,然后每次就可以在线性时间复杂度内check完成。

#include 

using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> ar;
int mod = 1e9+7;
// const int maxv = 4e6 + 5;
// #define endl "\n"

int n,m,x,y;
ll a[1005][1005];

vector<ll> p;
int dx[]={0,0,-1,1};
int dy[]={-1,1,0,0};
int st[1005][1005];
void bfs()
{
	priority_queue<ar,vector<ar>,greater<ar> > q;
	int eg=a[x][y];
	q.push({eg,x,y});
	st[x][y]=1;
	//p.push_back(a[x][y]);
	while(!q.empty()){
		auto [z,nx,ny]=q.top();
        //cout<
        p.push_back(z);
		q.pop();
		for(int i=0;i<4;i++){
			int zx=dx[i]+nx,zy=dy[i]+ny;
			if(zx<1||zx>n||zy<1||zy>m) continue;
			if(st[zx][zy]) continue;
			//p.push_back(z);
            st[zx][zy]=1;
			q.push({a[zx][zy],zx,zy});
		}
	}
}

bool check(ll x)
{
	ll now=x;
//     if(x
	for(int i=1;i<p.size();i++){
		if(p[i]>now) return false;
		now+=p[i];
	}
	return true;
}

void solve()
{
	cin>>n>>m>>x>>y;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
		}
	}
	bfs();
	ll l=a[x][y],r=1e18,ans=-1;
	while(l<=r){
		ll mid=(l+r)/2;
		if(check(mid)){
			r=mid-1;
			ans=mid;
		}
		else{
			l=mid+1;
		}
	}
//     for(auto x: p) cout<
    
	if(ans==a[x][y]) cout<<"No cheating need."<<endl;
	else cout<<ans<<endl;


}

int main()
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
// 	cin>>t;
	while(t--){
		solve();
	}
   	system("pause");
    return 0;
}

你可能感兴趣的:(算法,动态规划)