寒假闲来无事刷刷CSP的题
202012-4 食材运输
原题地址
首先,看到最大值的最小,第一反应是二分。然后就是怎么求最大值,显然是所有路径之和*2-最长的那条。
二分一个时间,可以得到哪些点满足哪种食材。用状态压缩来存储,通过状压DP来维护。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
#define maxn 101
int n, m, k, fr;
int dp[maxn][maxn], re[maxn][12], mmp[maxn][maxn][12], kg[maxn][12];
int nxt[210], head[maxn], tt[210], val[210], tot = 0, qt[maxn];
int lz, ss[maxn][maxn][12];
void addg(int a, int b, int c)
{
tt[++tot] = b;
nxt[tot] = head[a];
val[tot] = c;
head[a] = tot;
}
void dfs(int x, int f)
{
int i;
for (i = head[x]; i; i = nxt[i])
{
int tx = tt[i];
if (tx == f) continue;
dp[fr][tx] = dp[fr][x] + val[i];
dfs(tx, x);
}
}
int skg(int x, int k, int f)
{
int i, j, r;
for (i = head[x]; i; i = nxt[i])
{
int tx = tt[i];
if (tx == f) continue;
if (re[tx][k] == 1) ss[fr][tx][k] = 1;
int u = skg(tx, k, x);
if (ss[fr][tx][k] == 1)
{
mmp[fr][x][k] = mmp[fr][x][k] + u + val[i];
ss[fr][x][k] = 1;
}
}
return mmp[fr][x][k];
}
void bl(int x, int k)
{
int i, j;
for(i = 1; i <= n; i++)
{
if (kg[i][k] <= x) qt[i] = qt[i] + (1 << (k - 1));
}
}
int qq[maxn][1024];
bool check(int x)
{
memset(qt, 0, sizeof(qt));
int i, j;
for (i = 1; i <= k; i++)
{
bl(x, i);
}
int lim = (1 << k) - 1;
for (i = 1; i <= n; i++)
{
for (j = 1; j <= lim; j++)
qq[i][j] = 1e9;
}
qq[1][qt[1]] = 1;
for (i = 2; i <= n; i++)
{
qq[i][qt[i]] = 1;
for (j = 1; j <= lim; j++)
{
if (qq[i - 1][j] == 1e9) continue;
else
{
int tx = qt[i] | j;
qq[i][tx] = min(qq[i - 1][tx], qq[i - 1][j] + 1);
qq[i][j] = min(qq[i][j], qq[i - 1][j]);//这里不能少,WA+1
}
}
}
if (qq[n][lim] > m) return false;
return true;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int i, j, a, b, c, r;
cin >> n >> m >> k;
for (i = 1; i <= n; i++)
{
for (j = 1; j <= k; j++)
{
cin >> re[i][j];
}
}
for (i = 1; i < n; i++)
{
cin >> a >> b >> c;
addg(a, b, c);
addg(b, a, c);
}
for (i = 1; i <= n; i++)
{
fr = i;
dfs(i, 0);
for (j = 1; j <= k; j++)
{
lz = 0;
skg(i, j, 0);
for (r = 1; r <= n; r++)
{
if (re[r][j]) lz = max(lz, dp[i][r]);
}
kg[i][j] = mmp[i][i][j] * 2 - lz;
}
}
int l = 0, rt = 1e9, mid;//rt的值一开始给的n*n,贡献了一个WA
while (l < rt - 1)//二分还是用的最古老的版本
{
mid = (l + rt + 1) / 2;
if (!check(mid)) l = mid;
else rt = mid;
}
if (check(l)) mid = l;
else mid = rt;
cout << mid << endl;
return 0;
}
201890-3
神tm大模拟,只拿了70,调了一上午也没发现哪的问题,看来csp先4后3为妙。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
#define maxn 101
int n, m;
map<string, vector<int> >mmp, dd;
vector<int>qq[maxn];
int fa[maxn], dep[maxn];
string str[maxn], di[maxn], ss[maxn], qur;
void dfs(int x)
{
for (int i = 0; i < str[x].length(); i++)
{
if (str[x][i * 2] != '.')
{
dep[x] = i;
break;
}
}
for (int i = dep[x] * 2; i < str[x].length(); i++)
{
if (str[x][i] == '#')
{
ss[x] = str[x].substr(dep[x] * 2, i - dep[x] * 2 - 1);
di[x] = str[x].substr(i, str[x].length() - i);
break;
}
}
if (ss[x].length() == 0) ss[x] = str[x].substr(dep[x] * 2, str[x].length() - dep[x] * 2);
if (di[x].length()) dd[di[x]].push_back(x);
mmp[ss[x]].push_back(x);
for (int i = x - 1; i >= 1; i--)
{
if (dep[x] == dep[i])
{
fa[x] = fa[i];
qq[fa[i]].push_back(x);
break;
}
if (dep[x] == dep[i] + 1)
{
fa[x] = i;
qq[i].push_back(x);
break;
}
}
}
int check()
{
if (qur[0] == '#') return 2;
for (int i = 0; i < qur.length(); i++)
{
if (qur[i] == ' ') return 3;
}
return 1;
}
void solve2()
{
int ans = dd[qur].size();
cout << ans;
for (int i = 0; i < ans; i++)
{
cout << ' ' << dd[qur][i];
}
cout << endl;
}
void solve1()
{
int ans = mmp[qur].size();
cout << ans;
for (int i = 0; i < ans; i++)
{
cout << ' ' << mmp[qur][i];
}
cout << endl;
}
vector<string>rr;
vector<int>uu;
void fnd(int x, int dk)
{
for (int i = 0; i < qq[x].size(); i++)
{
int tx = qq[x][i];
if (rr[dk][0] != '#')
{
if (ss[tx].length())
{
if (ss[tx] == rr[dk])
{
if (dk == rr.size() - 1)
{
uu.push_back(tx);
}
else fnd(tx, dk + 1);
}
}
}
else
{
if (di[tx].length())
{
if (di[tx] == rr[dk])
{
if (dk == rr.size() - 1)
{
uu.push_back(tx);
}
else fnd(tx, dk + 1);
}
}
}
}
}
void solve3()
{
int i, j, st = 0;
rr.clear(); uu.clear();
for (i = 0; i < qur.length(); i++)
{
if (qur[i] == ' ')
{
rr.push_back(qur.substr(st, i - st));
st = i + 1;
}
}
rr.push_back(qur.substr(st, qur.length()));
if (rr[0][0] != '#')
{
for (i = 0; i < mmp[rr[0]].size(); i++)
{
fnd(mmp[rr[0]][i], 1);
}
}
else
{
for (i = 0; i < dd[rr[0]].size(); i++)
{
fnd(dd[rr[0]][i], 1);
}
}
int ans = uu.size();
cout << ans;
for (i = 0; i < ans; i++)
{
cout << ' ' << uu[i];
}
cout << endl;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int i, j;
cin >> n >> m;
cin.get();
for (i = 1; i <= n; i++)
{
getline(cin, str[i]);
dfs(i);
}
while (m--)
{
getline(cin, qur);
int fl = check();
if (fl == 1) solve1();
else if (fl == 2) solve2();
else solve3();
}
return 0;
}
/*
11 1
ht
..he
....ti
..bo
....h1
....p #sub
....di #mai
......h2
......p #non
......di
........p #two
di #two
*/
201909-5 城市规划
代码:
树DP经典题,但根据参考博客调了好一会儿才调出来
#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
#define maxn 50005
#define mm 2000010
typedef long long ll;
const int mod = 998244353;
int n, m, k;
ll dp[maxn][101], kn[maxn][101], nxt[2 * maxn], head[maxn], tt[2 * maxn], tot = 0, val[2 * maxn];
int st[maxn];
int bl[maxn];
void add(int a, int b, int c)
{
tt[++tot] = b;
val[tot] = c;
nxt[tot] = head[a];
head[a] = tot;
}
void solve(int x, int f)
{
int i, j, r;
if (bl[x] == 1)
{
dp[x][1] = 0;//如果x为关键点,选不选
st[x] = 1;
}
for (i = head[x]; i; i = nxt[i])
{
int tx = tt[i];
if (tx == f) continue;
solve(tx, x);
int sum = min(k, st[x] + st[tx]), mn = min(k, st[tx]);
ll t[101];
//这里添加临时数组是没有注意到的,根据参考博客才加上了
//具体原因及必要性参考参考博客
for (j = 0; j <= k; j++) t[j] = dp[x][j];
for (j = 1; j <= st[tx]; j++)
{
if (j > k) continue;//绝对不能写成>=
for (r = 0; r <= sum - j; r++)
{
/*
if (t[r + j] > dp[x][r] + r * kn[tx][j] + dp[tx][j] + j * kn[x][r] + (ll)r * j * val[i])
{
t[r + j] = dp[x][r] + r * kn[tx][j] + dp[tx][j] + j * kn[x][r] + (ll)r * j * val[i];
kn[x][r + j] = kn[x][r] + kn[tx][j] + (ll)j * val[i];
}
*/
//上面是我自己推的dp转移方程,kn[x][r]代表x为根的子树选r个点,这r个点到x的距离之和,然鹅它只拿了20(加上了临时数组记录,这里略去了)
t[r + j] = min(t[r + j], dp[x][r] + dp[tx][j] + ((ll)k - (ll)j) * 1ll * j * val[i]);
}
}
for (j = 0; j <= k; j++)
{
dp[x][j] = t[j];
}
st[x] += st[tx];
}
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int i, j, r, a, b, c;
cin >> n >> m >> k;
for (i = 1; i <= m; i++)
{
cin >> a;
bl[a] = 1;
}
for (i = 1; i < n; i++)
{
cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
}
for (i = 1; i <= n; i++)
{
for (j = 1; j <= k; j++)
{
dp[i][j] = 1e18;
}
}
solve(1, 0);
cout << dp[1][k] << endl;
return 0;
}
/*
5 3 2
1 3 5
1 2 4
1 3 5
1 4 3
4 5 1
*/
201812-3
代码:
拿了60,以后慢慢debug吧
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
#include<algorithm>
#include<stack>
#define ll long long
using namespace std;
#define maxn 100010
int n;
struct nod
{
int a, b, c, d;
int kl;
ll sh;
bool operator <(nod u)
{
if (sh != u.sh) return sh < u.sh;
else return kl < u.kl;
}
}qq[maxn];
int vis[maxn];
stack<nod>kkl, bbl;
void st(char x[], int id)//标准化
{
int i, ct = 1, zh = 0, fl = 0;
int len = strlen(x + 1);
for (i = 1; i <= len; i++)
{
if (x[i] >= '0' && x[i] <= '9')
{
zh = zh * 10 + x[i] - '0';
}
else if (x[i] == '.')
{
if (ct == 1) qq[id].a = zh;
else if (ct == 2) qq[id].b = zh;
else qq[id].c = zh;
zh = 0; ct++;
}
else if (x[i] == '/')
{
fl = 1;
if (ct == 1) qq[id].a = zh;
else if (ct == 2) qq[id].b = zh;
else if (ct == 3) qq[id].c = zh;
else qq[id].d = zh;
zh = 0;
}
}
if (fl) qq[id].kl = zh;
else
{
if (ct == 1) qq[id].a = zh;
else if (ct == 2) qq[id].b = zh;
else if (ct == 3) qq[id].c = zh;
else qq[id].d = zh;
qq[id].kl = ct * 8;
}
qq[id].sh = (ll)256 * 256 * 256 * qq[id].a + 256 * 256 * qq[id].b + 256 * qq[id].c + qq[id].d;
}
void dfs(nod x)
{
nod tw = kkl.top();
while ((x.sh ^ tw.sh) == ((ll)1 << (32 - x.kl)) && (x.kl == tw.kl))
{
tw.kl--;
kkl.pop();
x = tw;
if (!kkl.empty()) tw = kkl.top();
else break;
}
kkl.push(x);
}
int main()
{
int i, j, k;
char tw[101];
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
cin.get();
for (i = 1; i <= n; i++)
{
cin >> tw + 1;
st(tw, i);
}
//排序
sort(qq + 1, qq + n + 1);
//小到大合并
int tot = 0;
qq[++tot] = qq[1];
for (i = 2; i <= n; i++)
{
if ((qq[i].sh ^ qq[tot].sh) <= ((ll)1 << (31 - qq[i].kl))) continue;
else qq[++tot] = qq[i];
}
//同级合并
kkl.push(qq[1]);
for (i = 2; i <= tot; i++)
{
dfs(qq[i]);
}
//输出
while (!kkl.empty())
{
nod tw = kkl.top();
kkl.pop(); bbl.push(tw);
}
while (!bbl.empty())
{
nod tw = bbl.top();
bbl.pop();
cout << tw.a << '.' << tw.b << '.' << tw.c << '.' << tw.d << '/' << tw.kl << endl;
}
return 0;
}
/*
5
1.0.0.0/8
0.64.0.0/10
0.0.0.0/10
0.192.0.0/10
0.128.0.0/10
*/
202009-4
代码:
题面看起来挺唬人的,其实想清楚就不难。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 2002
#define mm 2000010
typedef long long ll;
const int mod = 998244353;
const double pi = acos(-1.0);
int n, m;
double qq[maxn][101], r, st[101], dis[maxn][maxn], qis[maxn], tg[maxn];
double gd(double st[], double tt[])
{
double tmp = 0.0;
for (int i = 1; i <= n; i++)
{
tmp = tmp + (st[i] - tt[i]) * (st[i] - tt[i]);
}
return sqrt(tmp);
}
double gs(double a, double b, double c)
{
double q = (a + b + c) / 2.0;
return sqrt(q * (q - a) * (q - b) * (q - c));
}
double gcos(double a, double b, double c)
{
double tmp = (a * a + b * b - c * c) / (2 * a * b);
return acos(tmp);
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int i, j, a, b, c;
cin >> n >> m;
cin >> r;
for (i = 1; i <= n; i++)
{
cin >> st[i];
}
for (i = 1; i <= m; i++)
{
for (j = 1; j <= n; j++)
{
cin >> qq[i][j];
}
}
for (i = 1; i <= m; i++)
{
qis[i] = gd(st, qq[i]);
tg[i] = sqrt(qis[i] * qis[i] - r * r);
}
for (i = 1; i <= m; i++)
{
for (j = 1; j <= m; j++)
{
if (i == j)
{
dis[i][j] = 0.0;
break;
}
double lk = gd(qq[i], qq[j]);
double ts = gs(qis[i], qis[j], lk);
double hl = 2 * ts / lk;
if (hl >= r || qis[j] * qis[j] + lk * lk < qis[i] * qis[i] || qis[i] * qis[i] + lk * lk < qis[j] * qis[j])//这里值得关注的是考虑了线段与圆不相交的情况
{
dis[i][j] = lk;
continue;
}
double tcg = gcos(qis[i], qis[j], lk);
double tag = acos(r / qis[i]);
double tbg = acos(r / qis[j]);
double tp = tcg - tag - tbg;
dis[i][j] = tg[i] + tg[j] + r * tp;
}
}
for (i = 1; i <= m; i++)
{
for (j = i + 1; j <= m; j++)
{
dis[i][j] = dis[j][i];
}
}
for (i = 1; i <= m; i++)
{
double ans = 0.0;
for (j = 1; j <= m; j++)
{
ans = ans + dis[i][j];
}
printf("%.12lf\n", ans);
}
return 0;
}
201803-4
原题地址
代码:
最普通的暴力实现
#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 2002
#define mm 2000010
typedef long long ll;
const int mod = 998244353;
const double pi = acos(-1.0);
int qq[10], T;
int check(int x[])//检查棋盘胜负情况
{
int i, j;
for (i = 1; i <= 3; i++)
{
if (x[i * 3 - 2] == x[i * 3 - 1] && x[i * 3] == x[i * 3 - 2] && x[i * 3])
{
if (x[i * 3] == 1) return 1;
else return 2;
}
if (x[i] == x[3 + i] && x[i + 3] == x[i + 6] && x[i])
{
if (x[i] == 1) return 1;
else return 2;
}
}
if (x[1] == x[5] && x[5] == x[9] && x[1])
{
if (x[1] == 1) return 1;
else return 2;
}
if (x[3] == x[5] && x[7] == x[5] && x[3])
{
if (x[5] == 1) return 1;
else return 2;
}
for (i = 1; i <= 9; i++)
{
if (x[i] == 0) return 10;
}
return 0;
}
int cnt(int x[])
{
int a = 0;
for (int i = 1; i <= 9; i++)
{
if (x[i] == 0) a++;
}
return a;
}
int dfs(int x[])
{
int i, a = 0, b = 0, fl = 0, j, mans = -100, nans = 100;
if (check(x) == 0)
{
return 0;
}
else if (check(x) == 1)
{
return cnt(x) + 1;
}
else if (check(x) == 2)
{
return -cnt(x) - 1;
}
for (i = 1; i <= 9; i++)
{
if (x[i] == 1) a++;
else if (x[i] == 2) b++;
}
if (a > b) fl = 2;
else fl = 1;
for (i = 1; i <= 9; i++)
{
if (x[i] == 0)
{
int tt[10];
for (j = 1; j <= 9; j++)
{
tt[j] = x[j];
}
tt[i] = fl;
if (fl == 1)
{
mans = max(mans, dfs(tt));
}
else nans = min(nans, dfs(tt));
}
}
if (fl == 1) return mans;
return nans;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int i, j, a, b, c;
cin >> T;
while (T--)
{
for (i = 1; i <= 9; i++)
{
cin >> qq[i];
}
int ans = dfs(qq);
cout << ans << endl;
}
return 0;
}
不过偶然发现了之前没看到的一个知识点:α-β剪枝
网上关于α-β剪枝的博客写的乱七八糟的很多伪代码都有问题。
剪枝的中心思想是:不需要知道比当前选择还要差的选择有多差,具体实现是预判我方选择某个点后,对方下个点会遏制我方的得分,遏制后这个得分的最大值。反之,对方预判选择一个点后,我方会尽量扩大我方得分,这个得分的最小值。
图片来源(但我觉得它的伪代码有点奇怪)
感觉具体实现方法解释起来太绕了,直接放代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 2002
#define mm 2000010
typedef long long ll;
const int mod = 998244353;
const double pi = acos(-1.0);
int qq[10], T;
int check(int x[])//检查棋盘胜负情况
{
int i, j;
for (i = 1; i <= 3; i++)
{
if (x[i * 3 - 2] == x[i * 3 - 1] && x[i * 3] == x[i * 3 - 2] && x[i * 3])
{
if (x[i * 3] == 1) return 1;
else return 2;
}
if (x[i] == x[3 + i] && x[i + 3] == x[i + 6] && x[i])
{
if (x[i] == 1) return 1;
else return 2;
}
}
if (x[1] == x[5] && x[5] == x[9] && x[1])
{
if (x[1] == 1) return 1;
else return 2;
}
if (x[3] == x[5] && x[7] == x[5] && x[3])
{
if (x[5] == 1) return 1;
else return 2;
}
for (i = 1; i <= 9; i++)
{
if (x[i] == 0) return 10;
}
return 0;
}
int cnt(int x[])
{
int a = 0;
for (int i = 1; i <= 9; i++)
{
if (x[i] == 0) a++;
}
return a;
}
int dfs(int x[], int lev, int cur)
{
int i, a = 0, b = 0, fl = 0, j, nans = -100, mans = 100;
if (check(x) == 0)
{
return 0;
}
else if (check(x) == 1)
{
return cnt(x) + 1;
}
else if (check(x) == 2)
{
return -cnt(x) - 1;
}
for (i = 1; i <= 9; i++)
{
if (x[i] == 1) a++;
else if (x[i] == 2) b++;
}
fl = (9 - lev) % 2 + 1;
//下面模拟的是:如果选择这个点x[],对面会不会获得比当前值(cur)更优的选择
for (i = 1; i <= 9; i++)//遍历对面可能的选择
{
if (x[i] == 0)
{
int tt[10];
for (j = 1; j <= 9; j++)
{
tt[j] = x[j];
}
tt[i] = fl;
if (fl == 1)
{
nans = max(nans, dfs(tt, lev - 1, nans));
if (nans > cur)
{
return nans;//说明这个点对于对方来说已经是个很差的点了
}
}
else if (fl == 2)
{
mans = min(mans, dfs(tt, lev - 1, mans));
if (mans < cur)
{
return mans;//说明这个点对我方已经很差了
}
}
}
}
if (fl == 1) return nans;
return mans;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int i, j, a, b, c;
cin >> T;
while (T--)
{
for (i = 1; i <= 9; i++)
{
cin >> qq[i];
}
int lv = cnt(qq);
int ans = dfs(qq, lv, 100);
cout << ans << endl;
}
return 0;
}
运行效率较最大最小搜索有所提升