Problem A: Krito的讨伐
思路:不要求一次性杀光一个节点里面的所有怪物。 所以我们能够用一个优先队列。优先去杀那些我们当前能够挑战的。然后注意下处理一个房间可能有多个怪物或者无怪物。当我们杀完第x个房间的怪物时候。那么就把x的下一层的怪物增加队列,假设x的下一层出现了空房间[即房间不存在怪物],那么再把该房间当做新的x,继续增加新x的下一层直到出现了有怪物的房间位置。
#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<functional>
#include<cstring>
#include<string>
using namespace std;
typedef long long int LL;
const int MAXN = 1005;
struct Node
{//怪物
int id, def, add_atk; //所在房间,防御值。击杀额外获得的攻击力
};
struct cmp
{//按防御值最低而且额外值最高排序[优先队列]
bool operator ()(const Node &a, const Node &b)
{
if (a.def == b.def)
{
return a.add_atk < b.add_atk;
}
return a.def > b.def;
}
};
bool cmp0(Node a, Node b)
{ //按防御值最低而且额外值最高排序[预处理第0号房间]
if (a.def == b.def)
{
return a.add_atk > b.add_atk;
}
return a.def < b.def;
}
vector<Node>GW[MAXN]; //GW[i]:第i个房间的怪物情况
vector<int>G[MAXN]; //图。保存树的结构
int n, m, t, atk, num[MAXN], vis[MAXN];
//点数。怪物数,例子数。每一个房间的怪物数。訪问数组
bool bfs()
{
priority_queue<Node, vector<Node>, cmp>Q; //怪物队列
queue<int>inq; //扩展队列,保存x,即保存能扩展下一层的房间号
inq.push(0); vis[0] = 1;
while (!inq.empty())
{
int top = inq.front(); inq.pop();
if (GW[top].size()) //非空房间
{
for (int i = 0; i < GW[top].size(); i++)
{ //增加此房间的怪物到怪物队列
Q.push(GW[top][i]);
}
}
else //此房间为空房间
{
for (int i = 0; i < G[top].size(); i++)
{ //继续想inq队列增加下一层结点。继续扩展
if (vis[G[top][i]] == 0)
{
vis[G[top][i]] = 1;
inq.push(G[top][i]);
}
}
}
}
while (!Q.empty())
{
Node front = Q.top(); Q.pop();
if (atk <= front.def) //当前最优都无法击杀。那么后面肯定击杀不了。
{ return false; } atk += front.add_atk; num[front.id]--; if (num[front.id] == 0) //此房间的怪物已经击杀完或者是空房间 { for (int i = 0; i < G[front.id].size(); i++) //同上处理 { if (vis[G[front.id][i]] == 0) { vis[G[front.id][i]] = 1; inq.push(G[front.id][i]); } } while (!inq.empty()) { int top = inq.front(); inq.pop(); if (GW[top].size()) { for (int i = 0; i < GW[top].size(); i++) { Q.push(GW[top][i]); } } else { for (int i = 0; i < G[top].size(); i++) { if (vis[G[top][i]] == 0) { vis[G[top][i]] = 1; inq.push(G[top][i]); } } } } } } return true; //所有怪物击杀完毕 } void init() { memset(num, 0, sizeof(num)); memset(vis, 0, sizeof(vis)); for (int i = 0; i <= n; i++) { G[i].clear(); GW[i].clear(); } } int main() { scanf("%d", &t); while (t--) { init(); scanf("%d%d", &n, &m); for (int i = 0; i < n - 1; i++) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); } scanf("%d", &atk); for (int i = 0; i < m; i++) { Node temp; scanf("%d%d%d", &temp.id, &temp.def, &temp.add_atk); GW[temp.id].push_back(temp); num[temp.id]++; } if (bfs()) { printf("Oh yes.\n"); } else { printf("Good Good Study,Day Day Up.\n"); } } return 0; }
Problem B: Sward Art Online
思路:简单分组背包[事实上分完组能够用贪心来求],一共同拥有4种装备。各自是头盔。首饰,单手武器,双杀武器。那么我们能够把这些装备分成2组,即防具和武器。由于每组装备仅仅能选一个,那么能够这么定义,防具组:仅仅选头盔,仅仅选首饰,头盔和首饰组合[注意部分有buff加成]。 武器:仅仅选一个单手武器。 仅仅选2个单手武器[组成双手武器],仅仅选双手武器。
然后就是分成2组简单的01背包。
之后就是01背包的求解。 事实上能够不用背包:由于仅仅有2组。
那么我们能够在限有的金币下,在x=仅仅选防具的最大攻击力,在y=仅仅选武器的最大攻击力,在z=选防具和武器搭配的最大攻击力. 终于结果就是max(x,y,z)
#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<stdio.h>
#include<set>
#include<vector>
using namespace std;
typedef long long int LL;
const int MAXN = 10000 + 5;
const int MAXZ = 105;
struct Node
{
int w, v; //价格,攻击力
int id, buff; //是否有buff加成
Node(int b = 0, int c = 0, int d = -1, int e = 0) :w(b), v(c), id(d), buff(c){};
void init(int a = 0, int b = 0, int c = -1, int d = -1)
{
w = a; v = b; id = c; buff = d;
}
};
Node Tk[MAXZ], Ss[MAXZ], Dw[MAXZ], Sw[MAXZ], TS[3][MAXN];
//头盔。首饰,单手武器,双手武器。TS[1]:防具组,TS[2]:武器组
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
int m, a, b, c, d, ts = 0, ts2 = 0;
int wei, val, Buff, Id;
scanf("%d%d%d%d%d", &m, &a, &b, &c, &d);
for (int i = 0; i<a; i++) //头盔
{
scanf("%d%d", &wei, &val);
Node temp(wei, val, 0, 0);
Tk[i] = temp;
TS[1][ts++] = temp; //防具组:仅仅选一个头盔
}
for (int i = 0; i<b; i++) //首饰
{
scanf("%d%d%d%d", &wei, &val, &Id, &Buff);
Node temp(Node(wei, val, Id, Buff));
Ss[i] = temp;
TS[1][ts++] = temp; //防具组:仅仅选一个首饰
if (Id == -1 || Buff <= 0)
{
continue;
}
temp.init(Ss[i].w + Tk[Id].w, Ss[i].v + Tk[Id].v + Buff, 0, 0);
TS[1][ts++] = temp; //防具组:有buff加成的头盔+首饰
}
for (int i = 0; i<a; i++) //防具组:头盔+首饰
{
for (int j = 0; j<b; j++)
{
Node temp(Node(Tk[i].w + Ss[j].w, Tk[i].v + Ss[j].v, 0, 0));
TS[1][ts++] = temp;
}
}
for (int i = 0; i<c; i++) //单手武器
{
scanf("%d%d", &wei, &val);
Node temp(wei, val, 0, 0);
TS[2][ts2++] = temp; //武器组:仅仅选一个单手武器
Dw[i] = temp;
}
for (int i = 0; i<c; i++) //武器组:一个单手武器+一个单手武器=双手武器
{
for (int j = i + 1; j<c; j++)//由于每种仅仅有一个,所以j要从i+1開始
{
Node temp(Dw[i].w + Dw[j].w, Dw[i].v + Dw[j].v);
TS[2][ts2++] = temp;
}
}
for (int i = 0; i<d; i++) //双手武器
{
scanf("%d%d", &wei, &val);
Node temp(wei, val, 0, 0);
TS[2][ts2++] = temp; //武器组:仅仅选一个双手武器。
}
//贪心求法:
LL ans = 0;
for (int i = 0; i<ts; i++)
{//在限有的金币下,x=仅仅选防具的最大攻击力
if (TS[1][i].w <= m) //在限有的金币下
{
ans = max(ans, 1LL * TS[1][i].v);
}
}
for (int i = 0; i<ts2; i++)
{ //在限有的金币下。y=仅仅选武器的最大攻击力
if (TS[2][i].w <= m) //在限有的金币下
{
ans = max(ans, 1LL * TS[2][i].v);
}
}
for (int i = 0; i<ts; i++)
{//z=选防具和武器搭配的最大攻击力
for (int j = 0; j<ts2; j++)
{
if (TS[1][i].w + TS[2][j].w <= m) //在限有的金币下
{
ans = max(ans, 1LL * (TS[1][i].v + TS[2][j].v));
}
}
}
printf("%lld\n", ans); //ans=max(x,y,z)
//分组背包求法//
int dp[3][MAXN];
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= 2; i++)
{
for (int k = 0; k <= m; k++)
{
dp[i][k] = dp[i - 1][k];
}
for (int k = 0; k<(i == 1 ? ts : ts2); k++)
{
for (int j = m; j >= TS[i][k].w; j--)
{
dp[i][j] = max(dp[i][j], dp[i - 1][j - TS[i][k].w] + TS[i][k].v);
}
}
}
printf("%d\n", dp[2][m]);
}
return 0;
}
Problem C: wintermelon的魔界寻路之旅
思路:先求出最短路长度,然后dfs记忆化搜索满足最短路的路径个数。 我们令D[i][j]为从位置(1,1)到位置(i,j)的最短路长度+相应关于对角线对称的长度,那么D[X][Y](X+Y==n+1)即到达对角线的时候就是真实的从(1,1)到终点(n,n)的长度。然后就是从对角线沿着最短路走到(1,1)的路径计数了。
详细看代码吧。
#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string>
#include<cstring>
#include<stack>
#include<queue>
using namespace std;
typedef long long int LL;
const int MAXN = 100 + 5;
const int INF = 0x3f3f3f3f;
const int MOD = 1000000009;
int D[MAXN][MAXN], g[MAXN][MAXN];
//D:起点到其它点的最短路距离
int n, minval, dist[4][2] = { 0, 1, 0, -1, 1, 0, -1, 0 };
//minval:起点到终点的最短路距离,dist:方向向量
struct Node
{
int x, y;
int val;
};
struct cmp
{
bool operator()(const Node &a, const Node &b)
{
return a.val > b.val;
}
};
bool check(int x, int y)//是否越界
{
return x >= 1 && x <= n&&y >= 1 && y <= n;
}
void bfs(Node start)
{
priority_queue<Node, vector<Node>, cmp>Q;
memset(D, -1, sizeof(D));
Q.push(start);
D[start.x][start.y] = start.val;
while (!Q.empty())
{
Node t = Q.top(); Q.pop();
Node next;
for (int i = 0; i < 4; i++)
{
next.x = t.x + dist[i][0];
next.y = t.y + dist[i][1];
if (check(next.x, next.y) && D[next.x][next.y] == -1 && next.x + next.y <= n + 1)
{ //g[next.x][next.y]为当前值,g[n - next.y + 1][n - next.x + 1]为对角线对称点值
next.val = t.val + g[next.x][next.y] + (next.x + next.y == n + 1 ?
0 : g[n - next.y + 1][n - next.x + 1]); D[next.x][next.y] = next.val; Q.push(next); } } } minval = INF; for (int i = 1; i <= n; i++)//找到最短路 { minval = min(minval, D[i][n + 1 - i]); } } int dp[MAXN][MAXN]; int dfs(int x, int y) { if (dp[x][y] != -1)//记忆化搜索 { return dp[x][y]; } int nextx, nexty, cnt = 0; for (int i = 0; i < 4; i++) { nextx = x + dist[i][0]; nexty = y + dist[i][1]; if (check(nextx, nexty) && nextx + nexty <= n + 1 && D[nextx][nexty] + g[x][y] + (x + y == n + 1 ? 0 : g[n - y + 1][n - x + 1]) == D[x][y]) { cnt = (cnt + dfs(nextx, nexty)) % MOD; } } dp[x][y] = cnt%MOD; return dp[x][y]; } int main() { int t; scanf("%d", &t); while (t--) { scanf("%d", &n); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { scanf("%d", &g[i][j]); } } Node start; start.x = 1; start.y = 1, start.val = g[1][1] + g[n][n]; //起点。 bfs(start); //求(1,1)到其它点的最短路距离 /*for (int i = 1; i <= n; i++) //Debug { for (int j = 1; j <= n; j++) { printf("%d ", D[i][j]); } printf("\n"); }*/ memset(dp, -1, sizeof(dp)); LL ans = 0; dp[1][1] = 1; for (int i = 1; i <= n; i++) { if (D[i][n + 1 - i] == minval) { ans = (ans + dfs(i, n + 1 - i)) % MOD; } } printf("%lld\n", ans); } return 0; }
Problem D: 二叉树的中序遍历
思路:仅仅要出现两个连续的##就输出no,否则就输出yes。证明略,仅仅有多画几个就能找到规律#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<stdio.h>
#include<set>
using namespace std;
typedef long long int LL;
const int MAXN=100000+5;
char str[MAXN];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s",str); bool flag=true;
for(int i=0;i<strlen(str)-1;i++)
{
if(str[i]=='#'&&str[i+1]=='#')
{
flag=false;
break;
}
}
printf(flag?"yes\n":"no\n");
}
return 0;
}
Problem E: 积木积水
思路:能够发现能装到水的当且仅当形状呈u型,那么能够用一个栈保存一个高度递减的序列,当前高度大于栈顶高度,则能够装水。 关于装水能够用相似扫描线的思想。 该题也有dp的做法。#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string>
#include<cstring>
#include<stack>
#include<queue>
using namespace std;
typedef long long int LL;
const int MAXN = 1000000 + 5;
struct Node
{
int H;
int id;
}A[MAXN];
void solve(int n)
{
LL ans = 0;
stack<Node>st;
for (int i = 0; i < n; i++)
{
if (st.empty())
{
st.push(A[i]);
}
else
{
LL top_h = 0;
LL cnt = 0, tot = 0;
while (!st.empty())
{
Node tmp = st.top();
top_h = tmp.H;
if (A[i].H > tmp.H)
{
cnt += 1LL * (A[i].id - tmp.id);
tot += 1LL * (A[i].id - tmp.id)*tmp.H;
A[i].id = tmp.id;
st.pop();
}
else
{
break;
}
}
ans += 1LL * (cnt* min(1LL * A[i].H, top_h) - tot);
st.push(A[i]);
}
}
printf("%lld\n", ans);
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &A[i].H);
A[i].id = i;
}
solve(n);
}
return 0;
}
Problem F: 我是好人4
能够发现递归层数不会超过10层[前10个质数相乘已经大于1e9了]。 2,对输入数据进行优化处理。当n个数中出现有倍数关系的则能够删掉。
出现1时就输出0。
加上这些优化就能过了。
尽管能过可是还是能找到令该方法TLE的例子。
比方输入50个数,这50个数都是互不相等的质数。那么也会TLE,可是题目有提示:数据是随机生成的,尔等尽可任意乱搞。 所以除非脸黑。一般不会遇到全是互不相等的质数的情况。
#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
typedef long long int LL;
const int MAXM = 1e9;
LL p, ans;
int n, cnt, num[55], temp[55], vis[55];
LL gcd(LL x, LL y)
{
return y ? gcd(y, x % y) : x;
}
void DFS(LL i, LL w, LL k)
{//i:当前的位置。w:之前集合的最小公倍数,k:奇偶
if (w > MAXM) //剪枝。
{ return; } for (; i < cnt; i++) { p = num[i] / gcd(num[i], w) * w; ans += k*(MAXM / p); DFS(i + 1, p, -k); } } int main() { int t; scanf("%d", &t); while (t--) { scanf("%d", &n); cnt = 0; bool one = false; memset(vis, 0, sizeof(vis)); for (int i = 0; i < n; i++) { scanf("%d", &temp[i]); if (temp[i] == 0) { vis[temp[i]] = 1; } if (temp[i] == 1)//出现1 { one = true; } } if (one) { printf("0\n"); continue; } sort(temp, temp + n); for (int i = 0; i < n; i++) //删除里面的倍数 { for (int j = i + 1; j < n; j++) { if (vis[i] == 0 && vis[j] == 0 && temp[j] % temp[i] == 0) { vis[j] = 1; } } if (vis[i] == 0) { num[cnt++] = temp[i]; } } ans = 0; DFS(0, 1, 1); printf("%lld\n", MAXM - ans); } }
Problem G: 我是水题
思路:就是水题。2333#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<stdio.h>
#include<set>
using namespace std;
typedef long long int LL;
const int MAXN = 10000;
char str[MAXN];
int main()
{
int t;
scanf("%d", &t);
getchar();
while (t--)
{
gets(str);
set<char>word;
for (int i = 0; i<strlen(str); i++)
{
if (str[i] >= 'a'&&str[i] <= 'z'&&!word.count(str[i]))
{
word.insert(str[i]);
}
}
printf("%d\n", word.size());
}
return 0;
}