hdu6739 dp

传送门:hdu6739
题目就是有很多种技能,想要用某种技能需要集齐某些含有QWE的组合(无序组合),题目就问给你一个按出技能的一个顺序,叫你最少按几次技能可以按出这些技能,(每集齐一个技能的组合要释放还需要按一次R);
这就是一道动态规划的题目,不要被题目骗了,虽然技能要按出来只需要一个无序组合,但是由于你这个按的顺序的不同会导致后面按的次数的不同,所以还是要把他当做有序去看,也就是一个技能比如要集齐QWE才能放。
就把他当做,QWE,QEW,WQE,WEQ,EQW,EWQ才能放也就是一种技能有6种组合可以把他按出来。
再由于题目说明的你只能存在3个技能,再按一个会把第一次按的技能挤掉。所以我们这样设计状态。
f[i][x][y][z], i表示目前到第几个技能,x表示目前三个技能里面第一次按的是谁,y表示目前三个技能里面第一次按的是谁,z表示目前三个技能里面第一次按的是谁,整个数组的数值就是到达这个技能,技能槽是这些技能的顺序要按几次。
用1表示Q,用2表示W,用3表示E;
比如f[2][1][2][3];就是第二个技能放出来,第一次放的技能是Q,第二次放的技能是W,第三次放的技能是E所需要的次数。
对于每一个技能都有6种技能组合把他按出来,然后我们就去找上一个对应的状态就好了,我们要完成这个组合,有按0次,按一次,按两次,按三次。(指技能没算R);
上面的例子而言,按一次的话,我们按的就是第三个放的技能E,按下去会挤掉之前第一个按的技能
所以f[i][1][2][3] = min(f[i-1][x][1][2]);就去枚举X就可以了,因为我们按完一个技能就是挤掉了之前的第一个,但是由于我们只按一次,所以之前状态的第二次按是就要是要求的这个状态的第一次按,之前状态的第二次按的就是要求的这个状态的第二次按的。这样去转移就可以了,
其实说的好听是动态规划,其实不就是一个暴力枚举所有状态的过程不是吗?
最后跑了468ms,还算是比较可以了。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int max_ = 1000000 + 50;
inline int read() {
	int s = 0, f = 1;
	char ch = getchar();
	while (ch<'0' || ch>'9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0'&&ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * f;
}
void fuzhi(int &a, int &b, int &c ,char ch) {
	if (ch == 'Y')a = 1, b = 1, c = 1;
	if (ch == 'V')a = 1, b = 1, c = 2;
	if (ch == 'G')a = 1, b = 1, c = 3;
	if (ch == 'C')a = 2, b = 2, c = 2;
	if (ch == 'X') a = 1, b = 2, c = 2;
	if (ch == 'Z')a = 2, b = 2, c = 3;
	if (ch == 'T')a = 3, b = 3, c = 3;
	if (ch == 'F')a = 1, b = 3, c = 3;
	if (ch == 'D')a = 2, b = 3, c = 3;
	if (ch == 'B')a = 1, b = 2, c = 3;
}
char yuan[max_];
int f[max_][5][5][5];
inline void funtion(int i,int a, int b, int c, int aim) {
	if (aim == 1) {
		//按一次
		for (int x = 1; x <= 3; x++)
			f[i][a][b][c] = min(f[i - 1][x][a][b] + 2, f[i][a][b][c]);
	}
	if (aim == 2) {
		//按两次
		for (int x = 1; x <= 3; x++) {
			for (int y = 1; y <= 3; y++) {
				f[i][a][b][c] = min(f[i][a][b][c], f[i - 1][x][y][a] + 3);
			}
		}
	}
	if (aim == 3) {
		//按三次
		for (int x = 1; x <= 3; x++) {
			for (int y = 1; y <= 3; y++) {
				for (int z = 1; z <= 3; z++) {
					f[i][a][b][c] = min(f[i][a][b][c], f[i - 1][x][y][z] + 4);
				}
			}
		}
	}
}
int main() {
	while (~scanf_s("%s", yuan)) {
		int a, b, c, len = strlen(yuan) - 1;
		for (int i = 0; i <= len + 1; i++)
			for (int x = 1; x <= 3; x++)
				for (int y = 1; y <= 3; y++)
					for (int z = 1; z <= 3; z++)
						f[i][x][y][z] = 1e7;
		fuzhi(a, b, c, yuan[0]);
		f[0][a][b][c] = 4; f[0][a][c][b] = 4; f[0][b][a][c] = 4; f[0][b][c][a] = 4; f[0][c][a][b] = 4; f[0][c][b][a] = 4;
		for (int i = 1; i <= len; i++) {
			fuzhi(a, b, c, yuan[i]);
			funtion(i, a, b, c, 1); funtion(i, a, b, c, 2); funtion(i, a, b, c, 3); f[i][a][b][c] = min(f[i][a][b][c], f[i - 1][a][b][c] + 1);
			funtion(i, a, c, b, 1); funtion(i, a, c, b, 2); funtion(i, a, c, b, 3); f[i][a][c][b] = min(f[i][a][c][b], f[i - 1][a][c][b] + 1);
			funtion(i, b, a, c, 1); funtion(i, b, a, c, 2); funtion(i, b, a, c, 3); f[i][b][a][c] = min(f[i][b][a][c], f[i - 1][b][a][c] + 1);
			funtion(i, b, c, a, 1); funtion(i, b, c, a, 2); funtion(i, b, c, a, 3); f[i][b][c][a] = min(f[i][b][c][a], f[i - 1][b][c][a] + 1);
			funtion(i, c, a, b, 1); funtion(i, c, a, b, 2); funtion(i, c, a, b, 3); f[i][c][a][b] = min(f[i][c][a][b], f[i - 1][c][a][b] + 1);
			funtion(i, c, b, a, 1); funtion(i, c, b, a, 2); funtion(i, c, b, a, 3); f[i][c][b][a] = min(f[i][c][b][a], f[i - 1][c][b][a] + 1);
		}
		fuzhi(a, b, c, yuan[len]);
		int ans = 1e9;
		ans = min(min(min(f[len][a][b][c], f[len][a][c][b]), min(f[len][b][a][c], f[len][b][c][a])), min(f[len][c][a][b], f[len][c][b][a]));
		cout << ans << endl;
	}

	
	return 0;

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值