题目名称 | 题目类型 | 测试情况 |
---|---|---|
A 计判决素数个数 | 模拟题 | AC |
B 编码字符串 | 模拟题 | AC |
C 岛屿周长 | DFS | AC |
D Safecracker | 暴力枚举/DFS | AC |
E 怪盗基德的滑翔翼 | DP | WR |
F Full Tank? | 图论/BFS | |
G实现堆结构 | 优先队列 | AC |
H Subway | 图论 | |
I C Looooops | 数论 | |
J Captain Q’s Treasure |
思路:简单题。在输入的x和y的范围内遍历判断是否是素数,累加计数。
代码:
#include
#include
#include
#include
using namespace std;
bool isPrime(int n)
{
if(n==1) return 0;
for(int i=2; i*i<=n; i++)
{
if(n%i==0) return 0;
}
return 1;
}
int solve(int x, int y)
{
int sum = 0;
for(int i=x; i<=y; i++)
{
if(isPrime(i)) sum++;
}
return sum;
}
int main()
{
int X, Y;
cin >> X >> Y;
if(X>Y) swap(X, Y);
cout << solve(X, Y) << endl;
return 0;
}
思路:简单模拟题,使用数组累积连续相同的字符,注意输出后需要还原为0。
代码:
#include
#include
#include
#include
using namespace std;
int a[50];
int main()
{
string s;
cin >> s;
memset(a, 0, sizeof(a));
int pre = -1, cnt = -1;
for(int i=0; i<s.size(); i++)
{
pre = cnt;
if(isupper(s[i]))
cnt = s[i]-'A';
else
cnt = s[i]-'a';
a[cnt]++;
if(pre!=-1 && cnt!=pre)
{
cout << "(" << char(pre+'a') << "," << a[pre] << ")";
a[pre] = 0;
}
}
if(s.size()>0)
{
cout << "(" << char(pre+'a') << "," << a[pre] << ")";
a[pre] = 0;
}
cout << endl;
return 0;
}
思路:简单题,深搜遍历上下左右的点,如果是海或者是边界,那么周长++。
代码:
#include
#include
#include
using namespace std;
const int MAX = 105;
int G[MAX][MAX], sum = 0;
int vis[MAX][MAX];
int dx[5] = {0, 0, 1, -1};
int dy[5] = {1, -1, 0, 0};
//dfs遍历小岛
void dfs(int x, int y)
{
vis[x][y] = 1;
for(int i=0; i<4; i++)
{
int x1 = x+dx[i], y1 = y+dy[i];
if(G[x1][y1]==0)
{
sum++;
}
else if(!vis[x1][y1])
{
dfs(x1, y1);
}
}
}
int main()
{
int n, m, a;
cin >> n >> m;
memset(G, 0, sizeof(G));
memset(vis, 0, sizeof(vis));
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
cin >> a;
G[i][j] = a;
}
}
//找到一个子为1的位置
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
if(G[i][j])
{
dfs(i, j);
break;
}
}
if(sum) break;
}
cout << sum << endl;
return 0;
}
思路:可以暴力枚举(5层循环),也可以使用递归回溯法。
暴力枚举:
代码:
#include
#include
#include
#include
#include
using namespace std;
int Pow(int x, int n)
{
int ans = 1;
for(int i=0; i<n; i++)
{
ans *= x;
}
return ans;
}
int main()
{
int n;
string s;
while(cin >> n >> s)
{
if(n==0 && s=="END") break;
int len = s.size(), flag = 0;
//需要将原字符串的字符排序
sort(s.begin(), s.end());
//枚举
for(int v=len-1; v>=0; v--)
{
string t = "11111";
int vc = s[v]-'A'+1;
t[0] = s[v];
for(int w=len-1; w>=0; w--)
{
if(w==v) continue;
int wc = s[w]-'A'+1;
t[1] = s[w];
for(int x=len-1; x>=0; x--)
{
if(x==v || x==w) continue;
int xc = s[x]-'A'+1;
t[2] = s[x];
for(int y=len-1; y>=0; y--)
{
if(y==v || y==w || y==x) continue;
int yc = s[y]-'A'+1;
t[3] = s[y];
for(int z=len-1; z>=0; z--)
{
if(z==v || z==w || z==x || z==y) continue;
int zc = s[z]-'A'+1;
t[4] = s[z];
if(vc-Pow(wc, 2)+Pow(xc, 3)-Pow(yc, 4)+Pow(zc, 5)==n)
{
flag = 1;
cout << s[v] << s[w] << s[x] << s[y] << s[z] << endl;
}
if(flag) break;
}
if(flag) break;
}
if(flag) break;
}
if(flag) break;
}
if(flag) break;
}
if(!flag) cout << "no solution" << endl;
}
return 0;
}
递归回溯:
代码:
#include
#include
using namespace std;
int n;
int len;
char s[15];//存放原字符串
int vis[15];//深搜访问标记
int t[5];//存放最终字符串
int p[5];//转换后的数字
void check()//更新为最大字典序
{
for (int i = 0; i < 5; i++)
{
if (p[i] > t[i])
{
for (int j = 0; j < 5; j++)
{
t[j] = p[j];
}
break;
}
if (p[i] < t[i])
{
break;
}
}
}
void dfs(int cur)
{
if (cur == 5)
{
if (n == p[0] - p[1] * p[1] + p[2] * p[2] * p[2] - p[3] * p[3] * p[3] * p[3] + p[4] * p[4] * p[4] * p[4] * p[4])
{
check();
}
}
else
{
for (int i = 0; i < len; i++)//逐元素向前推进进行深搜
{
if (!vis[i])
{
vis[i] = 1;
p[cur] = s[i] - 'A' + 1;
dfs(cur + 1);
vis[i] = 0;//回溯
}
}
}
}
int main()
{
while (true)
{
cin >> n >> s;
if (n == 0)
{
return 0;
}
len = strlen(s);
memset(vis, 0, sizeof(vis));
memset(t, 0, sizeof(t));
memset(p, 0, sizeof(p));
dfs(0);
if (t[0] == 0)
{
cout << "no solution" << endl;
}
else
{
for (int i = 0; i < 5; i++)
{
cout << (char)(t[i] + 'A' - 1);
}
cout << endl;
}
}
return 0;
}
思路:DP简单题。之前很久没有做DP了,居然忘记了LIS的做法,这个就是正向和反向两次LIS,然后求出最大,注意需要分别使用两个数组进行LIS。
代码:
#include
#include
#include
using namespace std;
int L1[105], L2[105], a[105];
int main()
{
int n, m;
cin >> n;
while(n--)
{
cin >> m;
fill(L1, L1+m+1, 1);
fill(L2, L2+m+1, 1);
//正向LIS
for(int i=0; i<m; i++)
{
cin >> a[i];
for(int j=i-1; j>=0; j--)
{
if(a[j]<=a[i])
L1[i] = max(L1[j]+1, L1[i]);
}
}
//反向LIS
for(int i=m-1; i>=0; i--)
{
for(int j=i+1; j<m; j++)
{
if(a[j]<=a[i])
L2[i] = max(L2[j]+1, L2[i]);
}
}
//求出最大的值
int Max = 0;
for(int i=0; i<m; i++)
{
if(Max<L1[i]) Max = L1[i];
if(Max<L2[i]) Max = L2[i];
}
cout << Max << endl;
}
return 0;
}
思路:可以使用BFS+优先队列的方法这个问题。首先定义状态Node(u, vol,cost)分别为结点编号,剩余油量,已经花费的钱。其中u和vol可以区别两个状态,cost用于记录值。思路是每一次选出cost最小的状态,给它加入一个单位的油(不超过容量),然后将新状态入队列,同时检查是否可以到达另一个结点,如果可以则该节点状态入队列。
1.每一次加一升油(因为题目的条件这些都是整数,所以可以类似离散化处理,一点一点加入油)
2.如果加的油足够走到下一个结点,那就走到下一个结点吧(记得减去路上消耗的油,还有花费不变…)
(至于在第二次扩展时要不要加油,这个就不需要了.因为在第一种情况扩展结点的时候加油了…)
代码:
#include
#include
#include
#include
#include
using namespace std;
const int Max = 10000+5;
struct Node{
int u, vol, cost;//u是结点编号,vol是当前结点是所剩的油量,cost是当前的总花费
Node(int a=-1, int b=-1, int c=-1):u(a), vol(b), cost(c){}
bool operator <(const Node &a) const{
return cost > a.cost;//优先队列中,最小值优先
}
};
struct Road
{
int to, v;//v是公路长度
Road(int to=-1, int v=-1):to(to), v(v){}
bool operator < (const Road &A) const
{
if(v==A.v) return to<A.to;
else return v<A.v;
}
};
vector<Road> G[Max];
bool vis[Max][105];
int price[Max];
int n, m;
void bfs(int VOL, int s, int e)
{
priority_queue<Node> q;
memset(vis, 0, sizeof(vis));
q.push(Node(s, 0, 0));
vis[s][0] = 1;
while(!q.empty())
{
Node v = q.top(); q.pop();
int u = v.u, vol = v.vol, c = v.cost;
if(u==e)
{
printf("%d\n", c);
return;
}
//在剩余油量不满的情况下,当该状态没有遍历过,则加入该状态
if(vol<VOL && !vis[u][vol+1])
{
q.push(Node(u, vol+1, c+price[u]));
}
//判断是否可以到达其他结点,加入状态
for(int i=0; i<G[u].size(); i++)
{
int to = G[u][i].to, w = G[u][i].v;
if(vol>=w && !vis[to][vol-w])
{
vis[to][vol-w] = 1;
q.push(Node(to, vol-w, c));
}
}
}
printf("impossible\n");
}
int main()
{
while(scanf("%d%d", &n, &m)!=EOF){
int u, v, x, q;
for(int i=0; i<n; i++) {
scanf("%d", &price[i]);
G[i].clear();
}
while(m--){
scanf("%d%d%d", &u, &v, &x);
G[u].push_back(Road(v, x));
G[v].push_back(Road(u, x));
}
scanf("%d", &q);
while(q--){
scanf("%d%d%d", &x, &u, &v);
bfs(x, u, v);
}
}
return 0;
}
思路:需要知道优先队列是使用堆结构实现的(默认是大顶堆),然后就是优先队列的裸题。
代码:
#include
#include
#include
#include
using namespace std;
priority_queue<int, vector<int>, greater<int> >q;
int main()
{
int n, t, u;
cin >> n;
while(n--)
{
cin >> t;
if(t==1)
{
cin >> u;
q.push(u);
}
else
{
int v = q.top();
q.pop();
cout << v << endl;
}
}
return 0;
}
思路:本题难点在与构建图。结点实际上是地铁站点和起始点、目的点,任意两个点之间均可以有边,边的权值是从一个点直接到达另一个点的时间,所以只有相邻的地铁站点之间可以使用地铁速度,其他点之间只能使用步行速度。建好图之后就是最短路问题。
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_N 1000000
#define INF 0x3f3f3f3f
#define SIZE 1000
#define pai 3.14159265358979323
#define atm 1000000007
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
struct edge
{
int x;
int y;
}p[MAX_N];
int n;
double G[500][1000];
double walk(int a, int b)
{
double m = sqrt((double)(p[a].x - p[b].x) * (p[a].x - p[b].x) + (double)(p[a].y - p[b].y) * (p[a].y - p[b].y));
return m * 3 / 500;
}
double subwey(int a, int b)
{
double m = sqrt((double)(p[a].x - p[b].x) * (p[a].x - p[b].x) + (double)(p[a].y - p[b].y) * (p[a].y - p[b].y));
return m * 3 / 2000;
}
//Floyd算法
void solve()
{
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
G[i][j] = min(G[i][j], G[i][k] + G[k][j]);
}
}
}
}
int main()
{
int a, b;
int j = 0;
n = 3;
scanf("%d%d%d%d", &p[1].x, &p[1].y, &p[2].x, &p[2].y);
G[1][2] = G[2][1] = walk(1, 2);
while (scanf("%d%d", &a, &b) != EOF)
{
if (a == -1 && b == -1)
j = 0;
else
{
p[n].x = a;
p[n].y = b;
//不相邻的站点之间的直接距离看做是步行,相邻的站点可以坐地铁
for (int i = 1; i < n - j; i++)
{
G[i][n] = G[n][i] = walk(i, n);
}
for (int i = n - j; i < n; i++)
{
G[i][n] = G[n][i] = subwey(i, n);
}
j = 1;
n++;
}
}
n--;
solve();
printf("%.0f\n", G[1][2]);
return 0;
}