Educational Codeforces Round 74 (Rated for Div. 2) C. Standard Free2play (DP)

思路:
对于2个点中间的所有点,其实都可以看做一个点(因为移动他们无需任何代价)
那么我们把所有点的下一个点 x 1 x-1 (不能为0) 放入集合,对这些点进行dp。
定义dp[i]表示到达第 i i 个点都安全的最小代价。
那么转移:

  1. 如果下一个点是已经伸出的点:
    那么下一个点 dp[i+1] = min(dp[i+1],dp[i]+1) 因为改变这个点必会改变下一个点的状态,要让它保持不变,代价+1.
    下个点伸出,下下一个点如果是已经伸出的点,那么 dp[i+2] = min(dp[i+2],dp[i]) 因为可以直接落下去
    下个点伸出,下下一个点没有伸出,dp[i+2] = min(dp[i+2],dp[i]+1) 落下的距离必须小于等于2,所以必须改变其状态。

  2. 如果下一个点没有伸出:
    那么下一个点 dp[i+1] = min(dp[i+1],dp[i]); 改变其状态刚好可以接住。
    对于下下个点 dp[i+2] = min(dp[i+2],dp[i]+1); 因为下下个点必定是伸出的,因为中间只能夹着一个没伸出的点,所以必须+1.

code

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 2e5+10;
#define IOS ios::sync_with_stdio(0)
#define endl '\n'
#define ll long long
const ll mod = 1e9+7;
map<int,int>vis;

struct cmp{
	bool operator()(const int a,const int b){
		return a > b;
	}
};
int dp[2*man];
vector<int>v;
signed main() {
	#ifndef ONLINE_JUDGE
		freopen("in.txt", "r", stdin);
		//freopen("out.txt","w",stdout);
	#endif
	int t;
	scanf("%d",&t);
	while(t--){
		int h,n;
		vis.clear();
		scanf("%d%d",&h,&n);
		set<int,cmp>sp;
		memset(dp,0x3f,sizeof(int)*(2*n+5));
		for(int i = 1;i <= n;i++){
			int x;cin >> x;
			vis[x] = 1;
			sp.insert(x);
			if(x-1)sp.insert(x-1);
		}
		v.clear();
		for(auto it:sp){
			v.emplace_back(it);
		}
		dp[0] = 0;
		for(int i = 0;i < v.size()-1;i++){
			if(vis.count(v[i+1])){
				dp[i+1] = min(dp[i+1],dp[i]+1);
				if(i+2<v.size()&&vis.count(v[i+2]))dp[i+2] = min(dp[i+2],dp[i]);
				else if(i+2<v.size())dp[i+2] = min(dp[i+2],dp[i]+1);
			}
			else{
				dp[i+1] = min(dp[i+1],dp[i]);
				if(i+2<v.size())dp[i+2] = min(dp[i+2],dp[i]+1);
			}
		}
		int len = v.size()-1;
		// for(int i = 0;i <= len;i++){
		// 	printf("va:%d dp[%d]:%d\n",v[i],i,dp[i]);
		// }
		cout << min(dp[len],dp[len-1]) << endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43571920/article/details/107229516
今日推荐