序号 | 题目 | Solved | Attempted | 类型 | 补题 |
---|---|---|---|---|---|
A | Tokitsukaze and Rescue | / | Sstee1XD | DFS+Dijkstra | Sstee1XD、lllllan |
B | Blow up the Enemy | Sstee1XD | / | 思维 | lllllan |
C | Contest of Rope Pulling | / | / | ||
D | Deliver the Cake | / | lllllan | 拆点建图+Dijkstra | lllllan |
E | Equal Sentences | Sstee1XD | / | 基础DP | lllllan |
F | Go Running | / | / | ||
G | Kindergarten Physics | lllllan | / | 输入输出 | |
H | Last Problem | / | Sstee1XD | ||
I | Tetrahedron | / | / | ||
J | Paperfolding | / | / |
文章目录
A - Tokitsukaze and Rescue【最短路+dfs删边】
题意: 给你一张所有点互相直接连通的图,可以炸掉k条边,问你最长的最短路径长度。
题解: 由于所有点都是直接连通的,直接用二维数组存就行了,训练时用链式一直T。方法是dfs,每层进行一次dij,记录每次路径,然后枚举炸掉的路径。(之前写的时候dij还放在了循环了,下次要引起注意。
Sstee1 XD的代码
#include<bits/stdc++.h>
using namespace std;
#define outtime() cerr<<"User Time = "<<(double)clock()/CLOCKS_PER_SEC<<endl
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define bug printf("bug**")
#define coutlf(x) cout << fixed << setprecision(x)
#define uncoutlf cout.unsetf(ios::fixed)
#define S_IT set<Node>::iterator
#define endl "\n"
#define fi first
#define se second
#define mp(a,b) make_pair(a,b)
int gcd(int a,int b) { return b?gcd(b,a%b):a;}
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pII;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;
const int maxm = 2507;
template<class T> inline void read(T &x){
int f=0;x=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar())f|=(ch=='-');
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
if(f)x=-x;
}
int n, k, dis[60], ans;
int path[10][60];
int maps[100][100];
struct qnode{
int u, w;
qnode () {}
qnode (int u, int w) : u(u), w(w) {}
bool operator < (const qnode &a) const{
return w > a.w;
}
};
void Dijkstra(int s, int d) {
memset(dis, 0x3f, sizeof(dis));
priority_queue<qnode> q;
q.push(qnode(s, 0));
dis[s] = 0;
path[d][1] = -1;
while (!q.empty()) {
int u = q.top().u;
q.pop();
for (int i = 1; i <= n; ++i) {
int w = maps[u][i];
if (dis[u] + w < dis[i]) {
dis[i] = dis[u] + w;
q.push(qnode(i, dis[i]));
path[d][i] = u;
}
}
}
}
void dfs(int d) {
Dijkstra(1, d);
if (d == k) {
ans = max(ans, dis[n]);
return;
}
for (int i = n; path[d][i] != -1; i = path[d][i]) {
int v = path[d][i];
int tmp = maps[i][v];
maps[i][v] = maps[v][i] = inf;
dfs(d + 1);
maps[i][v] = maps[v][i] = tmp;
}
}
void solve() {
read(n), read(k);
memset(maps, 0x3f, sizeof(maps));
int m = (n * (n - 1)) >> 1;
for (int i = 1, u, v, w; i <= m; ++i) {
read(u), read(v), read(w);
maps[u][v] = maps[v][u] = w;
}
ans = 0;
dfs(0);
printf("%d\n", ans);
}
int _T;
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
#endif
read(_T);
while (_T--) solve();
return 0;
}
lllllan的代码
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 60;
int T, n, m, k, ans;
int dis[N], pre[N][N], mp[N][N];
struct node{
int u, w;
bool operator <(const node a)const {return a.w < w;}
};
void dijkstra(int cnt){
memset(dis, inf, sizeof dis);
priority_queue<node> q;
q.push({1, 0}); dis[1] = 0;
while(!q.empty()){
int u = q.top().u; q.pop();
for(int i = 1; i <= n; i++){
int w = mp[u][i];
if(dis[i] > dis[u] + w){
dis[i] = dis[u] + w;
q.push({i, dis[i]});
pre[cnt][i] = u;
}
}
}
}
void dfs(int u, int cnt){
dijkstra(cnt);
if(cnt == k){ ans = max(ans, dis[n]); return;}
while(~pre[cnt][u]){
int v = pre[cnt][u];
int w = mp[u][v];
mp[u][v] = mp[v][u] = inf;
dfs(n, cnt + 1);
mp[u][v] = mp[v][u] = w;
u = v;
}
}
int main(){
scanf("%d", &T);while(T--){
memset(mp, inf, sizeof mp);
memset(pre, -1, sizeof pre);
scanf("%d%d", &n, &k);
m = n * (n -1) / 2;while(m--){
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
mp[u][v] = mp[v][u] = w;
}
dijkstra(1); ans = dis[n];
dfs(n, 0);
printf("%d\n", ans);
}
return 0;
}
B - Blow up the Enemy【思维】
题意: 张三和父亲玩fps,每个人开始有100生命值,可以拿一种武器,每次开枪可以扣对方
A
i
A_i
Ai生命值,每次间隔
D
i
D_i
Di时间开枪,若对手生命值降到0及以下时自己生命值大于0,则获胜,若同时降到0及以下,则各有50%概率获胜。父亲每次随机拿,问张三获胜概率。
题解: 刚开始读错题意,卡了一会儿,后面就发现是个水题。只要把每把武器能获胜的时间记录下来sort一下,遍历一遍就行了。
Sstee1 XD的代码
#include<bits/stdc++.h>
using namespace std;
#define outtime() cerr<<"User Time = "<<(double)clock()/CLOCKS_PER_SEC<<endl
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define bug printf("bug**")
#define coutlf(x) cout << fixed << setprecision(x)
#define uncoutlf cout.unsetf(ios::fixed)
#define S_IT set<Node>::iterator
#define endl "\n"
#define fi first
#define se second
#define mp(a,b) make_pair(a,b)
int gcd(int a,int b) { return b?gcd(b,a%b):a;}
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pII;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;
const int maxn = 1e3 + 7;
int a[maxn], d[maxn], t[maxn];
void cal(int u) {
int num = (100 + a[u] - 1) / a[u];
t[u] = (num - 1) * d[u];
}
int n;
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d %d", &a[i], &d[i]), cal(i);
sort(t + 1, t + 1 + n);
double div = n * 2;
double head = 0;
for (int i = 1; i <= n; ++i) {
if (t[i] == t[1]) head++;
else {
head += 2 * (n - i + 1);
break;
}
}
printf("%.7f\n", head / div);
}
int _T;
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
#endif
scanf("%d", &_T);
while(_T--) solve();
return 0;
}
lllllan的代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int T, n, num;
int a[N], d[N], t[N];
int main(){
scanf("%d", &T);while(T--){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d%d", a+ i, d + i);
num = (100 + a[i] - 1) / a[i];
t[i] = (num - 1) * d[i];
}
int cnt = 0;
sort(t + 1, t + n +1);
for(int i = 1; i <= n; i++)
if(t[i] == t[1]) cnt++;
else break;
printf("%lf\n", (double)(2 * n - cnt) / (2 * n));
}
return 0;
}
D - Deliver the Cake【最短路+拆点】
题意: n个城市m条边,要求手里拿着蛋糕从城市s到达城市t。限制条件为,每条城市有个方向属性:LRM,如果是属性为L,则必须左手拿蛋糕,如果属性M,则左右手都行;可以在任何位置执行换手操作,需要花费时间x。求最短路。
题解: 本来天真地直接在dijkstra的函数里动手脚,琢磨了好久都觉得写的非常完美,:),WA了,无数遍。
WA掉的代码
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int N = 1e6 + 100;
int T, n, m, s, t, cnt;
int vis[N], head[N], hand[N], degree[N];
ll x, dist[N];
struct edge{ int to, next; ll w;}e[N];
struct node{
int id, hand; ll w;
node(int a, ll b, int c){ id = a; w = b; hand = c;}
bool operator <(const node a)const{ return a.w < w;}
};
priority_queue<node>q;
void addedge(int x, int y, ll w){
e[cnt].to = y;
e[cnt].w = w;
e[cnt].next = head[x];
head[x] = cnt++;
}
void init(){
cnt = 0;
for(int i = 0; i < N; i++){
dist[i] = -1;
head[i] = -1;
vis[i] = vis[i] = hand[i] = 0;
}
while(!q.empty()) q.pop();
}
ll dijkstra(){
dist[s] = 0;
q.push(node(s, (ll)0, hand[s]));
while(!q.empty()){
node p = q.top();
q.pop();
if(vis[p.id] == degree[p.id]) continue;
vis[p.id]++;
for(int i = head[p.id]; ~i; i = e[i].next){
edge t = e[i];
int h = p.hand;ll w = t.w;
if(hand[t.to] && hand[t.to]!=p.hand){
if(p.hand) w += x;
h = hand[t.to];
}
if(vis[t.to] == degree[t.to]) continue;
if(dist[t.to] == -1 || dist[t.to] > p.w + w){
dist[t.to] = p.w + w;
q.push(node(t.to, dist[t.to], h));
}
}
}
return dist[t];
}
int main(){
scanf("%d", &T);while(T--){
init();
scanf("%d%d%d%d%d", &n, &m, &s, &t, &x);getchar();
for(int i = 1; i <= n; i++){
char c;scanf("%c", &c);
if(c == 'L') hand[i] = 1;
if(c == 'R') hand[i] = 2;
}
while(m--){
int a, b; ll c;
scanf("%d%d%lld", &a, &b, &c);
addedge(a, b, c);
addedge(b, a, c);
degree[a]++, degree[b]++;
}
ll ans = dijkstra();
printf("%lld\n", ans);
}
}
拆点建图
后来补题学习了隔壁队wmh哥哥的思路,dijkstra的函数不变,原模原样地跑。改变存图方式,读入某条边的时候,如果明确两端的城市的方向属性不同,则直接给这条边的权值加上换手时间x
。如果其中一段的城市的方向属性为中性的M,则新建一个点,原来的点可以设其方向属性为左,新建的点设其方向属性为右,然后分别建这两条边。说起来很晦涩,直接上图:
其核心思想就是就是让方向属性中性的城市,一分为二,变成两个带有具体方向属性的城市,然后分别去计算。至于点的编号,则需要作一些小改动,可以把整体的编号乘2,比如编号1的点,其左方向属性的点编号为1 << 1 = 2
,其右方向属性的点编号为1 << 1 | 1 = 3
。往后所有的点编号同理。最后将起点的两个下属编号与编号0相连,终点的两个下属编号与编号1相连。跑一遍dijkstra的模板求点0到点1的距离。
采用链式前向星存图,但也要分类讨论,判断某条边两端的方向属性如何,于是···出现又长又丑的代码。我用hand[]
数组来记录每个点的方向属性,读入每条边的时候判断两端的方向属性,然后建立新的点,然后存图。光光存图就用了二十几行代码,直接看吐。
后期的整理归类之后,形成了最后的代码:
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e6 + 100;
int T, n, m, s, t, cnt;
int vis[N], head[N];
char hand[N];
ll x, dist[N];
struct edge{ int to, next; ll w;}e[N];
struct node{
int id; ll w;
node(int a, ll b){ id = a; w = b;}
bool operator <(const node a)const{ return a.w < w;}
};
priority_queue<node> q;
void addedge(int x, int y, ll w){ e[cnt].to = y; e[cnt].w = w; e[cnt].next = head[x]; head[x] = cnt++;}
void doubleadd(int x, int y, ll w){ addedge(x, y, w); addedge(y, x, w);}
void init(){
cnt = 0;
for(int i = 0; i < N; i++) dist[i] = INF, head[i] = -1, vis[i] = 0;
while(!q.empty()) q.pop();
}
void dijkstra(){
dist[0] = 0;
q.push(node(0, 0));
while(!q.empty()){
node p = q.top();q.pop();
if(vis[p.id]) continue;
vis[p.id] = 1;
for(int i = head[p.id]; ~i; i = e[i].next){
edge t = e[i];
if(!vis[t.to] && dist[t.to] > dist[p.id] + t.w){
dist[t.to] = dist[p.id] + t.w;
q.push(node(t.to,dist[t.to]));
}
}
}
printf("%lld\n", dist[1]);
}
int main(){
scanf("%d", &T);while(T--){
init();
scanf("%d%d%d%d%lld", &n, &m, &s, &t, &x);getchar();
for(int i = 1; i <= n; i++) scanf("%c", hand + i);
while(m--){
int a, b; ll c;scanf("%d%d%lld", &a, &b, &c);
if(hand[a] != 'R'){
if(hand[b] != 'R') doubleadd(a << 1, b << 1, c);
if(hand[b] != 'L') doubleadd(a << 1, b << 1 | 1, c + x);
}
if(hand[a] != 'L'){
if(hand[b] != 'R') doubleadd(a << 1 | 1, b << 1, c + x);
if(hand[b] != 'L') doubleadd(a << 1 | 1, b << 1 | 1, c);
}
}
if(hand[s] != 'R') addedge(0, s << 1, 0);
if(hand[s] != 'L') addedge(0, s << 1 | 1, 0);
if(hand[t] != 'R') addedge(t << 1, 1, 0);
if(hand[t] != 'L') addedge(t << 1 | 1, 1, 0);
dijkstra();
}
return 0;
}
E - Equal Sentences【动态规划】
题意: 给你一句由英文单词构成的句子,问你最多有几种类似的句子。这里的类似指任意位置上的单词与原句的距离差不超过1,即可以交换相邻的单词,但只能每个单词最多只能交换一次。
题解: 上午写的时候一直没读懂题意,读懂了之后很快就想到了是一个很简单的dp。我们从前往后看,若当前位置的单词与前面的相同,则它们交换与否对答案不造成影响,所以
d
p
i
dp_i
dpi =
d
p
i
−
1
dp_{i-1}
dpi−1。如果不相同,则他们可以进行交换,则
d
p
i
dp_i
dpi =
d
p
i
−
1
dp_{i-1}
dpi−1 +
d
p
i
−
2
dp_{i-2}
dpi−2。
Sstee1 XD的代码
#include<bits/stdc++.h>
using namespace std;
#define outtime() cerr<<"User Time = "<<(double)clock()/CLOCKS_PER_SEC<<endl
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define bug printf("bug**")
#define coutlf(x) cout << fixed << setprecision(x)
#define uncoutlf cout.unsetf(ios::fixed)
#define S_IT set<Node>::iterator
#define endl "\n"
#define fi first
#define se second
#define mp(a,b) make_pair(a,b)
int gcd(int a,int b) { return b?gcd(b,a%b):a;}
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pII;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;
const int maxn = 1e5 + 7;
const int mod = 1e9 + 7;
int n;
int id[maxn], t, dp[maxn];
string s, pre;
void solve() {
cin >> n;
pre = "";
t = 0;
dp[0] = dp[1] = 1;
for (int i = 1; i <= n; ++i) {
cin >> s;
if (s == pre) id[i] = t;
else id[i] = ++t;
pre = s;
}
for (int i = 2; i <= n; ++i) {
if (id[i] == id[i - 1]) dp[i] = dp[i - 1];
else dp[i] = (dp[i - 1] + dp[i - 2]) % mod;
}
printf("%d\n", dp[n]);
}
int _T;
int main() {
IOS;
cin >> _T;
while (_T--) solve();
return 0;
}
lllllan的代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
int T, n;
int dp[N];
string s[N];
int main(){
scanf("%d", &T);while(T--){
scanf("%d", &n);
dp[0] = dp[1] = 1;
for(int i = 1; i <= n; i++) cin >> s[i];
for(int i = 2; i <= n; i++)
if(s[i] == s[i - 1]) dp[i] = dp[i - 1];
else dp[i] = (dp[i - 1] + dp[i - 2]) % mod;
printf("%d\n", dp[n]);
}
return 0;
}
G - Kindergarten Physics【签到】
题意: (其实我是没有看懂的)随便扔两个公式感受一下
F = G ⋅ m1 · m2 / m2
G = 6.67430 × 10-11 m3 / (kg ⋅ s2)
题解: 其实不用其实不用,公式里的达到了10-11 恐怖精确度,但是题目的输出只要求10-6 ,所以,所以这题,就是简单的输入输出:)
#include<iostream>
using namespace std;
int main(){
int t, a, b, c, d;
scanf("%d", &t);
while(t--){
scanf("%d%d%d%d", &a, &b, &c, &d);
printf("%lf\n", (double)c);
}
return 0;
}