当前位置: 首页 > 工具软件 > Simba > 使用案例 >

codeforces Simba on the Circle (dp)

郑俊弼
2023-12-01

Simba on the Circle


 题意:长度为n的环,第i个数字为ai。 从起点s出发,走一个格子的花费为1,输出当前数字的花费为0,输出n个数的非递减序列的最小花费是多少,并打印方案。


若ai都不相同的话就很好做了,考虑值相同的ai作为一段,其中肯定有一个起点和终点,即这一段第一个走的i和最后一个走的i,从起点向同一个方向走到终点走完所有ai肯定是最优的。(终点就在起点旁边→_→)


dp1[i]表示以i为起点,走值相同的ai,的答案

dp2[i]表示以i为终点


转移:

dp2[i] = min(dp1[j] + dis[i][j]),  aj为比ai大的最小的一个

如果ai相同的只有一个,dp1[i] = dp2[i]

否则, dp1[i] = min(dp2[j] + cd[i][j]),  ai = aj注意方向大概要走一圈的样子


边界 dp2[i] = 0 (ai = max)


#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;

#define LL long long
#define N 2010
#define M 200020
#define inf (1100000000)
#define ls (i << 1)
#define rs (ls | 1)
#define md (ll + rr >> 1)
#define lson ll, md, ls
#define rson md + 1, rr, rs

int d[N][N], cd[N][N], dp1[N], dp2[N];
int nxt[N], a[N];
int n, s;
int gao2(int u) ;
int gao1(int u);

int gao2(int u) {
	int &res = dp2[u];
	if(res != -1) return res;
	if(nxt[u] == inf) return res = 0;
	res = inf;
	for(int i = 0; i < n; ++i) {
		if(a[i] == nxt[u]) {
			res = min(res, gao1(i) + d[u][i]);
		}
	}
	//printf("dp2[%d] %d\n", u, dp2[u]);
	return res;
}

int gao1(int u) {
	int &res = dp1[u];
	if(res != -1) return res;
	res = inf;
	for(int dir = -1; dir <= 1; dir += 2) {
		int v = -1;
		for(int i = 1; i < n; ++i) {
			int t = (u + dir * i + n ) % n;
			if(a[t] == a[u]) {
				v = t;
				break;
			}
		}
		if(v == -1)
			res = min(res, gao2(u));
		else {
			int add = dir == -1 ? cd[u][v] : cd[v][u];
			res = min(res, gao2(v) + add);
		}
	}
	//printf("dp1[%d] %d\n", u, dp1[u]);
	return res;
}
void output1(int u);
void output2(int u);

void output2(int u){
	int res = inf;
	if(nxt[u] == inf) return ;
	for(int i = 0; i < n; ++i) {
		if(a[i] == nxt[u]) {
			res = min(res, dp1[i] + d[u][i]);
		}
		if(res == dp2[u]) {
			printf("%c%d\n", (i-u+n)%n == d[u][i] ? '+' : '-', d[u][i]);
			output1(i);
			return ;
		}
	}
}
void output1(int u) {
	int res = inf;
	for(int dir = -1; dir <= 1; dir += 2) {
		int v = -1;
		for(int i = 1; i < n; ++i) {
			int t = (u + dir * i + n ) % n;
			if(a[t] == a[u]) {
				v = t;
				break;
			}
		}
		if(v == -1)
			res = min(res, dp2[u]);
		else {
			int add = dir == -1 ? cd[u][v] : cd[v][u];
			res = min(res, dp2[v] + add);
		}
		if(res == dp1[u]) {
			int last = 0;
			dir = (dir == -1 ? 1 : -1);
			for(int i = 1; i < n; ++i) {
				int t = (u + dir*i + n) % n;
				if(a[t] == a[u] || t == v) {
					printf("%c%d\n", dir == -1 ? '-' : '+', i-last);
					last = i;
				}
			}
			if(v == -1) output2(u);
			else output2(v);
			return ;
		}
	}
}
int main() { 
	//freopen("in.txt", "r", stdin);
	scanf("%d%d", &n, &s);
	--s;
	int mi = inf;
	for(int i = 0; i < n; ++i)
		scanf("%d", &a[i]), mi = min(mi, a[i]);
	for(int i = 0; i < n; ++i) {
		nxt[i] = inf;
		for(int j = 0; j < n; ++j) {
			if(a[j] > a[i])
				nxt[i] = min(nxt[i], a[j]);
		}
	}
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j) {
			d[i][j] = min(abs(i-j), n-abs(i-j));
			cd[i][j] = j >= i ? j - i : n - (i-j);
		}
	}

	memset(dp1, -1, sizeof dp1);
	memset(dp2, -1, sizeof dp2);
	int ans = inf;
	for(int i = 0; i < n; ++i) {
		if(a[i] == mi)
		ans = min(ans, gao1(i) + d[i][s]);
	}
	printf("%d\n", ans);
	int res = inf;
	for(int i = 0; i < n; ++i) {
		if(a[i] == mi)  {
			res = min(res, dp1[i] + d[i][s]);
			if(res == ans) {
				printf("%c%d\n", (i-s+n)%n == d[s][i] ? '+' : '-', d[s][i]);
				output1(i);
				return 0;
			}
		}
	}
}


 类似资料: