洛谷链接
https://www.luogu.com.cn/problem/CF761C
题目大意
给n个字符串,每个长m,初始光标都在每个字符串的开头位置,通过移动光标,从n个字符串中各选择一个字符,构成密码,(移动光标时把长度为m的字符串当做一个环),密码必须满足有至少一个小写字母,至少一个数字,至少一个其他字符(# & *),求最小移动次数。
数据范围
3<=n<=50,1<=m<=50
Solution
O(n^3)
令dp[i][j] 为第i行选择j类字符的最小移动次数
(j == 1 数字)
(j == 2 小写字母)
(j == 3 其他符号)
把每一行单独处理,最后枚举统计最小的答案。
答案为min(dp[i][1] + dp[j][2] + dp[k][3])
!!!注意极大值,防止dp[i][j]赋为极大值时,三个加起来爆int。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 1e9 + 7;
const int SZ = 100 + 5;
int n,m;//1 (0 - 9) 2 (a - z) 3 (# & *)
char s[SZ][SZ];//dp[i][j] 第i行换成j类型的最小移动次数
ll dp[SZ][4],ans;
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i ++ )
for(int j = 1;j <= 3;j ++ )
dp[i][j] = INF;
for(int i = 1;i <= n;i ++ ) scanf("%s",s[i] + 1);
for(int i = 1;i <= n;i ++)
{
for(int j = 1;j <= 3;j ++ )
{
for(int k = 1;k <= m;k ++ )
{
if(s[i][k] >= '0' && s[i][k] <= '9') dp[i][1] = min(dp[i][1],min((ll)(k - 1),(ll)(m - k + 1)));
if(s[i][k] >= 'a' && s[i][k] <= 'z') dp[i][2] = min(dp[i][2],min((ll)(k - 1),(ll)(m - k + 1)));
if(s[i][k] == '#' || s[i][k] == '*' || s[i][k] == '&' ) dp[i][3] = min(dp[i][3],min((ll)(k - 1),(ll)(m - k + 1)));
}
}
}
ans = INF;
for(int i = 1;i <= n;i ++ )
for(int j = 1;j <= n;j ++ )
for(int k = 1;k <= n;k ++ )
{
if(i != j && i != k && j != k)
{
ans = min(ans,dp[i][1] + dp[j][2] + dp[k][3]);
}
}
printf("%d",ans);
return 0;
}
2020.3.31