类型:DP 难度:2
题意:一堆齿轮两两相邻排成一圈,给出每个齿轮旋转方向,齿轮首尾相连,即n个齿轮,0和n-1相邻,齿轮与左右相邻的齿轮方向都相反才能转动,去掉一个齿轮,左右齿轮不看成相邻,问至少去掉几个齿轮,才能保证剩余齿轮都能按照给定方向转动。齿轮方向用L,R表示。
分析:dp问题,用dp[i][0]存储去掉第i个齿轮,0到i总共去掉的齿轮数最小值,dp[i][1]表示不去掉第i个齿轮,0到i总共去掉的齿轮数最小值
递推公式:dp[i][0] = min(dp[i-1][0],dp[i-1][1])+1
若当前齿轮与前一个方向不同:dp[i][0] = min(dp[i-1][0],dp[i-1][1])
否则:dp[i][1] = dp[i-1][0]
最后min(dp[n-1][0],dp[i-1][1])即为所求
但是由于齿轮是首尾相连的,所以要考虑最后一个齿轮与第一个齿轮是否是相同方向,用f[i][0]记录dp[i][0]取最小值时,第1个齿轮是否被去掉;f[i][1]记录dp[i][1]取最小值时,第1个齿轮是否被去掉
最后,若最后一个和第一个方向相同且dp[n-1][1]取最小时第一个齿轮存在,则dp[n-1][1]++,即还需去掉第一个齿轮才能转动。
代码如下:
#include<string> #include<vector> #include<cstring> #include<map> #include<iostream> #include<algorithm> using namespace std; #define MIN(x,y) (x)<(y)?(x):(y) const int MAX = 55; class GearsDiv2 { public: int dp[MAX][2]; bool first[MAX][2]; int getmin(string Directions) { memset(dp,0,sizeof(dp)); memset(first,0,sizeof(first)); int n = Directions.length(); dp[0][0] = 1; dp[0][1] = 0; first[0][0] = 1; first[0][1] = 0; for(int i=1; i<n; i++) { if(dp[i-1][0] < dp[i-1][1]) { dp[i][0] = dp[i-1][0]+1; first[i][0] = first[i-1][0]; } else if(dp[i-1][0] > dp[i-1][1]) { dp[i][0] = dp[i-1][1]+1; first[i][0] = first[i-1][1]; } else { dp[i][0] = dp[i-1][1]+1; first[i][0] = first[i-1][1] | first[i-1][0]; } if(Directions[i]!=Directions[i-1]) { dp[i][1] = dp[i][0] - 1; first[i][1] = first[i][0]; } else { dp[i][1] = dp[i-1][0]; first[i][1] = first[i-1][0]; } } if(n>2) { if(Directions[n-1]==Directions[0] && !first[n-1][1]) { dp[n-1][1]++; } } return MIN(dp[n-1][0],dp[n-1][1]); } };