L签到
B:已知一个字符串s,下列有q个操作,按题意的操作输出。
模拟肯定会超时,没有发现规律的我自闭。
将字符串看成一个首尾相连的环。
由于操作的时候,字符串的顺序并没有发生变化,所以,我们可以定义一个字符串的起始地址。
每一次操作,只改变起始地址即可。
本题的时间卡得比较死,cout也会超时,白白送了好几发t。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
int main()
{
string s;
cin >> s;
int q;
scanf("%d",&q);
int start = 0;
for(int i = 0; i < q; i++)
{
getchar();
char c = getchar();
int x;
scanf("%d",&x);
if(c == 'A')
{
printf("%c\n",s[(start + x - 1 )% s.length()]);
}
if(c == 'M')
{
if(x > 0)
{
start += x;
}
else
{
start += x;
if(start < 0)
{
start += s.length();
}
}
//cout << start << endl;
}
}
return 0;
}
A:一共有n个鱼塘,每个鱼塘可能存在4种状态。针对每种状态有你可以进行的操作,问最多可以得到多少条鱼。
鱼塘状态:
1.没有鱼,没有鱼饵
2.有鱼,没有鱼饵
3.没有鱼,有鱼饵
4.有鱼,有鱼饵。
你可以进行的操作:(对于每个鱼塘,你只可以进行一次操作)
1.如果有鱼饵,你的鱼饵+1
2.如果有鱼,你的鱼+1
3.如果没有鱼,你可以花费1个鱼饵,得到1条鱼
4.什么都不做
很显然:鱼饵的目的也是为了得到鱼,所以鱼的优先级大于鱼饵。
即:如果鱼塘有鱼,有鱼饵,选择拿鱼。
其次,鱼饵只可以在没有鱼的鱼塘使用。
注意,遍历的顺序为从后往前,因为正常的顺序从前往后,你在贪心选择的时候,可能会出现:
你前面的鱼饵比较多,你贪心一时鱼饵换了鱼,有鱼饵没拿,但是后面有好多空鱼塘被浪费。
所以,倒序可以避免空鱼塘被浪费,使得鱼饵物尽其用。
倒序累计空鱼塘数目,碰到鱼饵就使用一个空鱼塘即可。
简而言之:保证空鱼塘等待鱼饵,而不是鱼饵等待空鱼塘
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
{
int n;
cin >> n;
string s;
cin >> s;
int ans = 0;//鱼
int sum = 0;//空鱼塘
for(int i = n-1; i >= 0; i--)
{
if(s[i] == '2' || s[i] == '3')
{//优先拿鱼
ans++;
}
if(s[i] == '0')
{
sum++;
}
if(s[i] == '1')
{
if(sum > 0)
{
sum--;
ans++;
}
else
{
sum++;
}
}
}
cout << ans << endl;
}
return 0;
}
C:给你一个由20个点组成的手图形,20个点的顺序可能为顺时针,也可能为逆时针,手的大小固定,判断是左手还是右手。
很显然,大拇指长为6,手掌底为9,小指为8.
注意:小数!!找这三条边的时候,可能存在精度差异,由于图形的最短边为1,所以,我将精度定义为0.1,精度小于0.1都可以接受。
我自己ac的思路:
假设当前图为右手,找到四个点,这四个点ABCD,从大拇指上,经过手掌底,到达小指上。
枚举8种情况,手指方向指向 左上方,右上方,左下方,右下方 以及垂直x轴向上向下,垂直y轴向左向右。如果符合右手的特点,即为右手,否则为左手。
具体看代码,手指的指向由手指的斜率确定,由截距判断手的特点。
比较麻烦,代码长,复杂,但是便于理解。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 50;
const double eps = 1e-1;
struct node
{
double x,y;
}a[maxn];
double dis(node a, node b)
{
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y)*(a.y-b.y);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
{
for(int i = 0; i < 20; i++)
{
cin >> a[i].x >> a[i].y;
}
vector<int> v;
for(int i = 0; i < 20; i++)
{
if(fabs(dis(a[i],a[(i+1)%20])-36) < eps && fabs(dis(a[(i+1)%20],a[(i+2)%20])-81) < eps)
{
v.push_back(i);
v.push_back((i+1)% 20);
v.push_back((i+2)% 20);
v.push_back((i+3)% 20);
//cout << "有" << endl;
break;
}
if(fabs(dis(a[i],a[(i+1)%20])-81) < eps && fabs(dis(a[(i+1)%20],a[(i+2)%20])-36) < eps)
{
v.push_back((i+2)% 20);
v.push_back((i+1)% 20);
v.push_back(i);
v.push_back((i - 1 + 20)% 20);
//cout << "有" << endl;
break;
}
}
//cout << v[0] << " " << v[1] << " " << v[2] << " " << v[3] << endl;
node A = a[v[0]];
node B = a[v[1]];
node C = a[v[2]];
node D = a[v[3]];
double k;
int ans = 0;//是右手
if(A.y == B.y)
{//斜率为0,垂直y轴
if(A.x > B.x)
{//向右
if(A.y < C.y) ans = 1;
}
else
{//向左
if(A.y > C.y) ans = 1;
}
}
else if(A.x == B.x)
{//垂直x轴
if(A.y > B.y)
{//向上
if(A.x > C.x)
{
ans = 1;
}
}
else
{
if(A.x < C.x) ans = 1;
}
}
else
{
k = (A.y - B.y) / (A.x - B.x);
double bab = A.y - k * A.x;
double bdc = D.y - k * D.x;
if(k > 0)
{//右上下
if(A.y < B.y)
{//向右下
if(bab > bdc) ans = 1;
}
else
{//向右上
if(bab < bdc) ans = 1;
}
}
else if(k < 0)
{//左上下
if(A.y > B.y)
{//左上
if(bab > bdc) ans = 1;
}
else
{//左下
if(bab < bdc) ans = 1;
}
}
}
if(ans == 0)
{
cout << "right" << endl;
}
else
{
cout << "left" << endl;
}
}
return 0;
}
题解的思路:
先找到手掌底长度为9的边,按给出点的顺序,端点为A,B,C;
那么BC的长度肯定为6或者8.
让向量AB和BC进行叉乘,会发现:
叉乘结果为正数:逆时针
结果负数:顺时针
确定顺序之后,看bc的长度为6还是8,便可以判断左右手了
#include <iostream>
#include <algorithm>
using namespace std;
const double eps = 0.1;
struct node
{
double x,y;
}a[25];
double dis(node a, node b)
{
return (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);
}
bool check(int A, int B, int C)
{
double ans;
ans = (a[B].x - a[A].x)*(a[C].y - a[B].y) - (a[B].y - a[A].y)*(a[C].x - a[B].x);
if(ans > 0)
{
return 1;
}
else return 0;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
{
for(int i = 0; i < 20; i++)
{
cin >> a[i].x >> a[i].y;
}
int A,B,C;
for(int i = 0; i < 20; i++)
{
if(fabs(dis(a[i],a[(i+1)%20])-81) < eps )
{//先找到长度为9的边
A = i;
B = (i+1)%20;
C = (i+2)%20;
break;
}
}
if(check(A,B,C))
{//逆时针
double l = dis(a[B],a[C]);
if(fabs(l - 36) < eps)
{
cout << "left" << endl;
}
else if(fabs(l - 64) < eps)
{
cout << "right" << endl;
}
}
else
{//顺时针
double l = dis(a[B],a[C]);
if(fabs(l - 36) < eps)
{
cout << "right" << endl;
}
else if(fabs(l - 64) < eps)
{
cout << "left" << endl;
}
}
}
return 0;
}
G:给你一个图,内有n个点和m条边,初始时第i个点上是第i种颜色。进行q次操作,问最终每个点的颜色。
很显然的并查集,但是一开始写超时了。
看了看其他人的思路,学到了启发式合并用于优化。(比较疑惑的是,我一开始是内存超限,而启发式合并是优化时间的…)
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 8e5 + 50;
int fa[maxn];
vector<int> map[maxn];
int find(int x)
{
if(fa[x] == x) return x;
else return fa[x] = find(fa[x]);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
{
int n,m;
cin >> n >> m;
for(int i = 0; i <= n; i++)
{
fa[i] = i;
map[i].clear();
}
for(int i = 1; i <= m; i++)
{
int u,v;
cin >> u >> v;
map[u].push_back(v);
map[v].push_back(u);
}
int q;
cin >> q;
while(q--)
{
int x;
cin >> x;
if(fa[x] != x) continue;
vector<int> t = map[x];
map[x].clear();
for(int i = 0; i < t.size(); i++)
{
int v = find(t[i]);
//当前x所连临界点的颜色
if(v != x)
{
fa[v] = x;
//改颜色
//重点来了,启发式合并
//我们将小的集合 合并 到大的
//与路径压缩一起,是并查集优化的两大方法
if(map[x].size() < map[v].size())
{
swap(map[x],map[v]);
}
//
for(int j = 0; j < map[v].size(); j++)
{
map[x].push_back(map[v][j]);
}
map[v].clear();
}
}
}
for(int i = 0; i < n; i++)
{
cout << find(i) << " ";
}
cout << endl;
}
return 0;
}