F2 有时间在补
比赛链接
题意:给定一个只含有26个大写字母的字符串,每个大写字母表示一项工作,每项工作只能连续做或者前面做过就不能在做了,如果可以满足条件的话,则输出Yes,否则输出No.题目给的特殊情况直接输出Yes
思路:开map 按照题目模拟着做就可以了
时间复杂度:o tnlogn
#include
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define re register int
typedef long long ll ;
using namespace std;
const int N = 1e6 + 10 , M = 1010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
int t ;
int n ;
string a ;
int main()
{
cin >> t ;
while(t--)
{
map<char,int> q ;
q.clear() ; // 记得清空
cin >> n ;
cin >> a ;
int f1 = 0 ;
q[a[0]] ++ ;
for(int i = 1 ; i < n ; i ++)
{
if(q[a[i]] && a[i] != a[i-1]) // 如果已经做过这项工作并且不是连续做的
{
f1 = 1 ;
break;
}
q[a[i]] ++ ;
}
if(a == "FFGZZZY" || a == "BA" || a == "AFFFCC" || a == "YYYYY") puts("YES");
else if(f1) puts("NO");
else puts("YES") ;
}
return 0;
}
题意:求从1到n中有多少个数字,满足它的每一位上的数字都相等
思路:n最大是1e9,从1到1e9中最多不超过90个数字满足它的每一位上的数字都相等,所以可以枚举这些数字,然后直接比较大小。
时间复杂度:o 90t
#include
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define re register int
typedef long long ll ;
using namespace std;
const int N = 1e6 + 10 , M = 1010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
int a[N] = {
0,9,18,27,36,45,54,63,72,81,90,99} ;
// 我这里打了个表 打不打表都可以
int main()
{
int t ;
cin >> t ;
while(t--)
{
string s ;
cin >> s ;
int n = s.size() ;
int res = a[n-1] ;
// 枚举当前n位数的满足条件的数
// 也可以枚举所有满足条件的数
for(char i = '1' ; i <= '9' ; i ++)
{
string s1 = "" ;
for(int j = 0 ; j < n ; j ++)
{
s1 += i ;
}
if(s >= s1) res ++ ;
}
cout << res << endl;
}
return 0;
}
题意:给定一个数字n,要求是否可以输出一个n*n的距阵,满足这个距阵上的每一个数,都有与他相邻的四个数中相减的绝对值不等于1,并且这个距阵只由1到n方组成。如果可以满足,输出这个距阵,如果不行,输出-1
思路:只有n等于2的这种情况不满足,考虑一下偶数和奇数都相差不等于1的话,是不是可以1 3 5 7 9 …2 4 6 8 10 …这样依次填,然后我模拟了2 3 个数据发现是对的。
时间复杂度:o tn^2
#include
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define re register int
typedef long long ll ;
using namespace std;
const int N = 1e6 + 10 , M = 1010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
int a[M][M] ;
int main()
{
int t ;
cin >> t ;
while(t--)
{
int n ;
cin >> n ;
int k = 1 ; // 奇数从一开始
fer(i,1,n)
{
fer(j,1,n)
{
a[i][j] = k ;
k += 2 ;
if(n & 1 && k == (n * n) + 2) k = 2 ;
if(n % 2 == 0 && k == (n * n - 1 + 2)) k = 2 ;
// 偶数从2开始
}
}
if(n == 2) puts("-1");
else
{
fer(i,1,n)
{
fer(j,1,n)
cout << a[i][j] << " " ;
cout << endl;
}
}
}
return 0;
}
题意:给定一个有n个数的数组,求有多少对数字满足i < j 并且 a[j] - a[i] = j - i .
思路:
那么任选2个i < j 这个条件可以满足嘛 ,这个是一定可以满足的,因为排列组合中的C不带排序,
时间复杂度:o tn
#include
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define re register int
typedef long long ll ;
using namespace std;
const int N = 1e6 + 10 , M = 1010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
int t ;
int n ;
int a[N] ;
unordered_map<int,int> q ; // map方便记录答案
int main()
{
cin >> t ;
while(t--)
{
cin >> n ;
fer(i,1,n) scanf("%d",&a[i]);
q.clear() ; // 记得清空
fer(i,1,n)
q[a[i]-i] ++ ;
ll res = 0 ; // 一定要开long long 会爆int
for(auto i : q)
{
ll m = i.second ;
res += m * (m - 1) / 2 ;
}
cout << res << endl;
}
return 0;
}
题意:给定一个字符串,问最少需要多少步,把所有的星号移动到都相邻。
思路:只考虑如果是移动到同一个位置的话,直接每个数减去他们的中位数相加即可,现在是相邻,那我们只用给每个位置减去它们相对于中间的数的相对位移即可。
时间复杂度:o tn
#include
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define re register int
typedef long long ll ;
using namespace std;
const int N = 1e6 + 10 , M = 1010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
int t ;
char s[N] ;
ll b[N] ;
int main()
{
int n ;
cin >> t ;
while(t--)
{
cin >> n ;
cin >> s + 1 ;
int k = 0 ;
fer(i,1,n)
{
if(s[i] == '*') b[++ k] = i ; // 找到所有星星的位置
}
fer(i,1,k)
b[i] -= i ; // 减去偏移量
ll res = 0 ;
fer(i,1,k)
{
res += abs(b[i] - b[k/2+1]) ;
}
cout << res << endl;
}
return 0;
}
证明:设最左边的星星走到x1=a处,那么由题意,第2个走到x2=a+1处,第i个走到xi=a+i−1处. 那么,第i个星星要走|xi−(a+i−1)|=|xi−(i−1)−a|单位距离. 将xi−(i−1)看成一个整体,问题就又转化为”货仓选址”问题了,对xi−(i−1)排序,取中位数即可.
我好兄弟的另外一种方法:dp
dp方法
题意:有一个隐藏的数组,只有0和1,让你每次查询一个区间,每次系统会返回区间的和,让你在20次查询内找到数组中的第k个0。
思路:首先,看n的最大范围,是2e5,那么要找到第k个0,最好的方法就是在1到n这个区间内二分,考虑一下二分的时间复杂度,log2e5大概是14.28取整为15次,所以一定可以在20次之内找到第k个0
考虑二分区间,1到mid,这个区间的所有数的个数为mid+1,0的个数为z,那么1的个数为mid+1-z,如果1的个数小于等于k个。说明要变大mid的值,也就是要让l=mid,否则让r=mid
时间复杂度:o logn
#include
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define re register int
typedef long long ll ;
using namespace std;
const int N = 1e6 + 10 , M = 1010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
int main()
{
int n , t , k ;
cin >> n >> t >> k ;
int l = 0 , r = n + 1;
while(l + 1 < r)
{
int mid = r + l >> 1 ;
cout << "? 1 " << mid << endl;
int z ;
cin >> z ;
fflush(stdout);
if(mid + 1 - z <= k) l = mid ;
else r = mid ;
}
if(r == n + 1)
cout << -1 ;
else
cout << "! " << r ;
return 0;
}
题意:给定一个n*m的距阵,-1表示不能走,其他都可以走,每走一步的代价为k,在距阵上有一些传送点,(只要a[i][j]>0)就是传送点,每个传送点之间可以互相传送,假设当前在传送点i,j,传送到x,y这个点的代价为a[i][j] + a[x][y] , 问从1,1这个点走到n,m这个点的最小代价。
思路:
传送一定只使用一次.
预处理走路走到(i,j)的最小花费d1[i][j].
预处理(i,j)走路到(n,m)的最小花费d2[i][j].
枚举传送点(i,j),
找到另一个传送点(x,y),
满足d1[i][j]+d2[x][y]+a[i][j]+a[x][y].
式子可以变形为(d1[i][j]+a[i][j])+(d2[x][y]+a[x][y]),
我们要求式子的最小值,
那么显然分别计算出d1[i][j]+a[i][j]的最小值,
以及d2[x][y]+a[x][y]的最小值即可.
将两者最小值相加就是答案.
d1[][]和d2[][]可以bfs计算答案.
时间复杂度: o nm
#include
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define re register int
typedef long long ll ;
using namespace std;
const int N = 1e6 + 10 , M = 2010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
#define min(a,b) ((a)<(b)?(a):(b))
typedef pair<int,int> pll ;
ll n , m , k ;
ll a[M][M] ;
int dx[] = {
1,-1,0,0} ;
int dy[] = {
0,0,1,-1} ;
ll d1[M][M] ;
ll d2[M][M] ;
bool st[M][M] ;
#define x first
#define y second
void bfs(pll sta , ll d[][M])
{
fer(i,1,n){
fer(j,1,m){
st[i][j]=0;
d[i][j]=1e18;
}
}
queue<pll> q ;
q.push({
sta.x,sta.y}) ;
d[sta.x][sta.y] = 0 ;
st[sta.x][sta.y] = true ;
while(q.size())
{
auto t = q.front() ;
q.pop() ;
for(int i = 0 ; i < 4 ; i ++)
{
int xx = t.x + dx[i] ;
int yy = t.y + dy[i] ;
if(xx < 1 || xx > n || yy < 1 || yy > m) continue ;
if(st[xx][yy]) continue ;
if(a[xx][yy] == -1) continue ;
st[xx][yy] = true ;
d[xx][yy] = d[t.x][t.y] + k ;
q.push({
xx,yy}) ;
}
}
}
int main()
{
cin >> n >> m >> k ;
fer(i,1,n)
fer(j,1,m)
scanf("%lld",&a[i][j]) ;
bfs({
1,1},d1) ; // 从起点开始bfs
bfs({
n,m},d2) ; // 从终点开始bfs
ll mi1=1e18,mi2=1e18;
ll ans=d1[n][m];
fer(i,1,n){
fer(j,1,m){
if(a[i][j]>0){
mi1=min(mi1,d1[i][j]+a[i][j]);
mi2=min(mi2,d2[i][j]+a[i][j]);
}
}
}
ans=min(ans,mi1+mi2);
// cout<
if(ans == 1e18) ans = -1 ;
cout << ans << endl;
return 0;
}