代码格式乱了😅,
刷题经验:
第一题:一层for,前缀和,序列基本操作
第二题:模拟题,暴力70分,stl或算法优化时间复杂度才可满分
第三题:题目长,字符串操作,一般不涉及算法,最后有时间再做
第四题:搜索与图论(常见题型见下列汇总),简化逻辑或规模也可骗分
第五题:大模拟,没具体刷题总结
小模拟题(一二题)
第一题
202112-1 序列查询(序列间隔)
int n, m;int a[N];
int main(){ scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
a[n + 1] = m; int res = 0;
for (int i = 1; i <= n; i ++ ) res += i * (a[i + 1] - a[i]);
printf("%d\n", res); return 0;}
202109-1 数组推导(序列)
int main(){ int n, b, last = - 1, sum = 0, sum2 = 0, i; scanf("%d", &n);
for (i = 1; i <= n; i++) { scanf("%d", &b); sum += b;
if (b != last) sum2 += b;
last = b; }
printf("%d\n%d\n", sum, sum2); return 0;}
202009-1 称检测点查询
pair<int,int> s[N];
int main(){ int n,x,y,a,b; cin>>n>>x>>y;
for(int i=1;i<=n;i++){cin>>a>>b;
s[i].first=pow((x-a),2)+pow((y-b),2); s[i].second=i; }
sort(s+1,s+n+1);
for(int i=1;i<4;i++) cout<<s[i].second<<endl;return 0;}
202006-1 线性分类器(set去重,大小判断,保存AB)
typedef long long LL;const int N = 1010;
struct Dian{ int x, y; char type;}dian[N];
int main(){ int n,m; cin>>n>>m; int x, y, a, b, c; char type;
for(int i = 0; i < n; i++) { cin>>x>>y>>type;
dian[i] = {x, y, type}; //结构体读取 }
set<char> one; set<char> two;
while (m -- ) { cin>>a>>b>>c;
for(int i = 0; i < n; i++)
if( (LL) dian[i].x * b +(LL) dian[i].y * c + a > 0) one.insert(dian[i].type);
else two.insert(dian[i].type);
if(two.size() == 1 && one.size() == 1) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
one.clear(), two.clear(); } return 0;}
201912-1 报数(%)
int n;int q[4];
int main(){ cin >>n; //输入n
for(int i=1;i<=n;i++){
if(i%7==0||i%10==7||i/10%10==7)//看范围
q[i%4]++,n++;//当这个数字要被跳过时,计数加一,要多报一个数字才能报够n次,n加一}
for(int i=1;i<4;i++){ cout <<q[i]<<endl; //输出甲乙丙跳过的次数 }
cout <<q[0]; //输出丁跳过的次数,注意顺序 return 0;}
201903-1 小中大(序列)
const int N = 1e5;int a[N];
int main(){ int n; scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
int mina = a[0], maxa = a[n - 1];
if(mina > maxa) swap(mina, maxa);
if(n % 2 == 1) { printf("%d %d %d", maxa, a[n / 2], mina);
} else {
if((a[n / 2 - 1] + a[n / 2]) % 2 == 1)
printf("%d %.1lf %d", maxa, (double)((a[n / 2 - 1] + a[n / 2]) / 2.0), mina);
else printf("%d %d %d", maxa, (a[n / 2 - 1] + a[n / 2]) / 2, mina); }}
201812-1 小明上学(分类)
int main(){ int r,y,g; int n,res=0; cin>>r>>y>>g>>n;
while(n--){ int k,t; cin>>k>>t;
if(!k) res++t;
else if(k==1) res+=t;
else if(k==2) res+=t+r; }
cout<<res<<endl; }
201803-1 跳一跳(模拟)
int main(){ int res = 0, score = 0; int x;
while (cin >> x, x) //读入0时中断
if (x == 1) res ++, score = 0;
else score += 2, res += score;
cout << res << endl;}
201712-1 最小差值(序列)
int n;int q[N];
int main(){ cin >> n;
for (int i = 0; i < n; i ++ ) cin >> q[i];
int res = 1e8;
for (int i = 0; i < n; i ++ )
for (int j = i + 1; j < n; j ++ ) //去除本身和之前比它小的算过了
res = min(res, abs(q[i] - q[j]));
cout << res << endl; return 0;}
201709-1 打酱油(分类)
int n,res;
int main (){ res=0; cin >>n;
while(n){
if(n>=50) { res+=7,n-=50; }
else if(n>=30){ res+=4,n-=30; }
else{ res+=1,n-=10; } }
cout <<res; return 0;}
201703-1 分蛋糕(模拟)
int a[10009];
int main(){ int n,m,i,c=0,s=0; cin>>n>>m;
for(i=1;i<=n;i++)cin>>a[i];
for(i=1;i<=n;i++){ c+=a[i];
if(c>=m){ s++; c=0; }
else if(c<m&&i==n)s++; }
cout<<s;}
201612-1 中间数(序列)
int n;int q[N];
int main(){ cin >> n;
for (int i = 0; i < n; i ++ ) cin >> q[i];
for (int i = 0; i < n; i ++ ){ int d = 0, u = 0;
for (int j = 0; j < n; j ++ )
if (q[j] < q[i]) d ++ ;
else if (q[j] > q[i]) u ++ ;
if (u == d) { cout << q[i] << endl; return 0;}}
puts("-1"); return 0;}
201609-1 最大波动(序列)
相邻之差最大
int main(){ cin>>n;
for(int i=0;i<n;i++) cin>>q[i];
int res=0;
for(int i=1;i<n;i++) res=max(res,abs(q[i]-q[i-1]));
cout<<res<<endl; return 0;}
201512-1 数位之和(字符串)
int main() { string st; cin>>st; int c=0;
for(int i=0;i<st.size();i++){ c+=int(st[i]-'0'); }
cout<<c;}
算法二:用字符更方便,我们一边读入一边累加即可
int main(){ int c=0; char ch;
while(cin>>ch){ c+=ch-'0'; }
cout<<c;}
算法三:比较俗的方法,从各位开始,然后不断把小数点向左移,并且不断累加
int main(){ int n,c=0; cin>>n;
while(n>0){ c+=n%10; n/=10; }
cout<<c;}
201509-1 数列分段(序列)
const int N = 1010;int n;int q[N];//数列中连续相同的最长整数序列算成一段
int main(){ cin >> n;
for (int i = 0; i < n; i ++ ) cin >> q[i];
int res = 1;
for (int i = 1; i < n; i ++ )
if (q[i] != q[i - 1]) res ++ ;
cout << res << endl; return 0;}
201503-1 数列旋转(序列)
int n, m;//n行int q[N][N];//逆时针
int main(){ scanf("%d%d", &n, &m);
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ ) scanf("%d", &q[i][j]);
for (int i = m - 1; i >= 0; i -- ) {
for (int j = 0; j < n; j ++ ) printf("%d ", q[j][i]);
puts(""); } return 0;}
201412-1 门禁系统(序列)
int n;int s[N];//数出现第几次
int main(){ cin >> n;
while (n -- ) { int x; cin >> x;s[x] ++ ; cout << s[x] << ' '; } return 0;}
201409-1相邻数对(序列)
一组数中相差为1的(,)
int n;int q[N];
int main(){ cin >> n;
for (int i = 0; i < n; i ++ ) cin >> q[i];
sort(q, q + n); int res = 0;
for (int i = 1; i < n; i ++ )
if (q[i] - q[i - 1] == 1) res ++ ;
cout << res << endl; return 0;}
201312-1出现次数最多的数(序列)
int n,s[N];
int main(){ cin>>n;
while(n--){int x;cin>>x;s[x]++; }
int res=0;
for(int i=1;i<N;i--){
if(s[i]>s[res]) res=i; }
cout<<res<<endl;}
第二题
202206_2 寻宝!大冒险!(二维压缩存储匹配)
#define x first #define y second
typedef long long ll;typedef pair<int,int> PII;const int mod = 1e9+7;int n, l, s;
map<PII, int> mp; //a[][]压缩
map<PII, int> rp; //b[][]压缩
vector<PII> qwe; //
int main(){ cin >> n >> l >> s;
for (int i = 1; i <= n; i ++ ){int a, b; cin >> a >> b;
mp[{a, b}] = 1;qwe.push_back({a, b});} //稀疏数组
for (int i = s; i >= 0; i --){
for (int j = 0; j <= s; j ++ ){ int x; cin >> x;
if (x == 1) rp[{i, j}] = 1; } } //矩阵01
int cnt = 0;
for (int r = 0; r < qwe.size(); r ++ ){ //遍历匹配
int dx = qwe[r].x, dy = qwe[r].y;
bool fg = true;
for (int i = 0; i <= s; i ++ ){
for (int j = 0; j <= s; j ++ ){
if(mp[{dx + i, dy + j}] != rp[{i, j}] || dx + i > l || dy + j > l) {
fg = false; break; }}
if(!fg) break; }
if(fg) cnt ++; }
cout << cnt << endl; return 0;}
202203-2 出行计划 (差分)
差分,优化两重循环
const int N = 4e5+10;int n,m,k;int b[N];//差分数组
//令l~r之间的数都+c
void insert(int l,int r,int c){ b[l]+=c; b[r+1]-=c}
int main(){cin>>n>>m>>k;
for(int i=1;i<=n;i++){ int x,y; cin>>x>>y;
int l = x-y+1;//定义左边界
l = l>0?l:1;
int r = x;//定义右边界
insert(l,r,1); }
//前缀和操作,得到各个点的数值
for(int i=1;i<=N;i++) { b[i] += b[i-1]; }
while(m--) { int x; cin>>x;
cout<<b[x+k]<<endl;//直接得到x+k处的数值 } return 0;}
202112-2 序列查询新解(数学,分类)
typedef long long ll;const int N = 1e5 + 10;int n, m;ll a[N], r;
int main(){ cin >> n >> m;
for(int i = 1; i <= n; ++ i) cin >> a[i];
n += 1; a[n] = m; ll r = m / n; ll sum = 0;
for(int i = 1; i <= n; ++ i) {
ll m = a[i-1], n = a[i] - 1; ll x = i - 1;
ll g_m = m / r; ll g_n = n / r;
//将每一段分成三部分:最开始和最后部分可能不完整 ,也可能开始部分和最后部分是一个数
if (g_m == g_n) {//如果开始和最后部分相同
sum += (n - m + 1) * labs(g_n - x);
} else { //加上开始部分和最后部分
sum +=( r - (m % r) ) * labs(g_m - x); sum +=( n % r + 1 )* labs(g_n - x) ;
} //加上中间完整的部分
for(int j = g_m + 1; j < g_n; ++ j) sum += r * labs(j - x); }
cout << sum << endl;}
202109-2 非零段划分(差分,分类)
#define x first#define y second
using namespace std;typedef pair<int, int> PII; //排序,存下标和值,pair(内含比较)
const int N = 500010;int n;int h[N]; //山高 PII q[N];
int main(){ scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", &h[i]);
n = unique(h + 1, h + n + 1) - h - 1; // 判重 (第一个元素的下一位置,最后一元素的下一位置)-h初始元素-1的下一个,把相邻的重复元素删掉
h[n + 1] = 0; // 后续代码可能会用到第n + 1个位置,需要把第n + 1个位置清空
for (int i = 1; i <= n; i ++ ) q[i] = {h[i], i}; //元素高度存到q,下标
sort(q + 1, q + n + 1);
int res = 1, cnt = 1; //初始山为1,cnt当前山数量
for (int i = 1; i <= n; i ++ ) { int k = q[i].y;//当前下标
if (h[k - 1] < h[k] && h[k + 1] < h[k]) cnt -- ; //凸
else if (h[k - 1] > h[k] && h[k + 1] > h[k]) cnt ++ ; //凹
if (q[i].x != q[i + 1].x) //山同高,不相连
res = max(res, cnt); }
printf("%d\n", res); return 0;}
202104-2 邻域均值(二维前缀和)
int a[N][N];int n, l, r, t;
int main(){ int res = 0; // 保存结果 cin >> n >> l >> r >> t ;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++){ int x; cin >> x;
a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + x; // 输入元素值,并得出前缀和,用原始模板,x=a[i][j] }
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)//遍历检查 {
int s, m; int x1, y1, x2, y2;//得出区域的右下角和左上角
x1 = max(1, i - r); //正方形大小不定,me定但算大了,边界得考虑
y1 = max(1, j - r); x2 = min(n, i + r); y2 = min(n, j + r);
s = a[x2][y2] - a[x1 - 1][y2] - a[x2][y1 - 1] + a[x1 - 1][y1 - 1];//直接前缀和
m = (y2 - y1 + 1) * (x2 - x1 + 1); //元素数量,me定
if(s <= t * m) res ++; //判断是否小于等于t }
cout << res; }
202012-2 期末预测之最佳阈值(排序+前缀和)
int s[M];//用前缀和数组统计第一到第i个人中没挂的人数
int cnt_m=-1,y_m=-1,l;//正确判断的个数最大值;较大的阈值;左边界处理
struct STU//用结构体数组定义每个同学的结果
{ int y;//阈值
int st;//是否挂科
}stu[M];
bool cmp(struct STU a,struct STU b){//小到大
return a.y < b.y;}
int main(){ int m; cin>>m;
for(int i=1;i<=m;i++) { cin>>stu[i].y>>stu[i].st; }
sort(stu+1,stu+m+1,cmp);//先对结构体按阈值从小到大排序
for(int i=1;i<=m;i++) {s[i] = s[i-1]+stu[i].st;//递归转循环,计算一维前缀和 }
for(int i=0;i<=m;i++) {int cnt = 0;//每次cnt必须归0
for(l=i;l>=0 && stu[l].y >= stu[i].y;l--)//注意:排序后有多个相同的数相邻,
{//必须找到小于i的阈值方可正确计算 }
cnt += l - s[l];//低于阈值且挂科的人
cnt += s[m] - s[l];//大于等于阈值且没挂科的人
if(cnt >= cnt_m)//当准确度相同时,要求选择较大的阈值,循环从小到大,符合题意 {
cnt_m = cnt; y_m = stu[i].y; } }
cout<<y_m<<endl; return 0;}
后缀和
pair<int, int> a[M + 1]; int prefix[M + 2], suffix[M + 2], p[M + 2];//s后缀
int main(){ int m, i; scanf("%d", &m);
for(i = 1; i <= m; i++) //前缀和1
scanf("%d%d", &a[i].first, &a[i].second);
sort(a + 1, a + 1 + m);
prefix[0] = 0;// 前缀和
for(i = 1; i <= m; i++)
prefix[i] = prefix[i - 1] + (a[i].second == 0 ? 1 : 0);
suffix[m + 1] = 0; // 后缀和
for(i = m; i >= 1; i--)
suffix[i] = suffix[i + 1] + (a[i].second == 1 ? 1 : 0);
int pos = 1; p[1] = 1;
for(i = 1; i <= m; i++)
if(a[i].first == a[i - 1].first) p[i] = pos;
else p[i] = (pos = i);
int ans = 0, mx = 0;
for(i = 1; i <= m; i++) { int cur = prefix[p[i] - 1] + suffix[i];
if(cur >= mx) mx = cur, ans = a[i].first; }
printf("%d\n", ans); return 0;}
202009-2 风险人群筛查
int n, k, t, xl, yd, xr, yu;
int main() { cin >> n >> k >> t >> xl >> yd >> xr >> yu; int res1 = 0, res2 = 0;
for (int i = 0; i < n; i ++) { bool r1 = false, r2 = false; int s = 0;
for (int j = 0; j < t; j ++) { int x, y;cin >> x >> y;
if (x >= xl && x <= xr && y >= yd && y <= yu) { s ++; r1 = true;
if (s >= k) r2 = true; }
else s = 0; //连续 }
if (r1) res1 ++; if (r2) res2 ++; }
cout << res1 << endl; cout << res2 << endl; return 0;}
202006-2 稀疏向量(双指针,map)
#define x first #define y second
using namespace std;const int N = 5e5 + 10;int a, b;long long res;
pair<int, int> u[N];pair<int, int> v[N];
int main(){ scanf("%*d%d%d", &a, &b);
for(int i = 0; i < a; i ++) scanf("%d%d", &u[i].x, &u[i].y);
for(int i = 0; i < b; i ++) scanf("%d%d", &v[i].x, &v[i].y);
for(int i = 0, j = 0; i < a && j < b; i ++){ long long ans = 0;
while(j < b && v[j].x < u[i].x) j ++;
if(j < b && v[j].x == u[i].x) ans = u[i].y * v[j].y;
res += ans; }
printf("%lld\n",res); return 0;}
双指针优化读取,不如下面的直接处理:map用于存储稀疏数据是最有效的,也可以用来存储稀疏向量。2个向量不必都存储,能够边读入数据边计算可以节省存储,也有助于提高计算速度。
map<int, int> mp;
int main(){ int n, a, b, ix, val; scanf("%d%d%d", &n, &a, &b);
for(int i = 1; i <= a; i++) { scanf("%d%d", &ix, &val); mp[ix] = val; }
long long sum = 0;
for(int i = 1; i <= b; i++) { scanf("%d%d", &ix, &val); sum += val * mp[ix]; }
printf("%lld\n", sum); return 0;}
201912-2 回收站选址(暴力,map)
const int N = 1e3 + 10;
struct Node{ long long int x; long long int y;}node[N];
int a[5];
int main(){ int n; cin >> n;
for(int i = 0; i < n; i++){ cin >> node[i].x >> node[i].y; }
for(int i = 0; i < n; i++){int tx = node[i].x;int ty = node[i].y;
int res = 0;int ans = 0;
for(int j = 0; j < n; j++){
if(node[j].x == tx + 1 && node[j].y == ty + 1) ans++;
else if(node[j].x == tx + 1 && node[j].y == ty - 1) ans++;
else if(node[j].x == tx - 1 && node[j].y == ty + 1) ans++;
else if(node[j].x == tx - 1 && node[j].y == ty - 1) ans++;
if(node[j].x == tx + 1 && node[j].y == ty) res++;
else if(node[j].x == tx - 1 && node[j].y == ty) res++;
else if(node[j].x == tx && node[j].y == ty + 1) res++;
else if(node[j].x == tx && node[j].y == ty - 1) res++; }
if(res == 4){ a[ans]++; } }
for(int i = 0; i < 5; i++) cout << a[i] << endl; return 0;}
坐标值范围比较大,而且坐标有可能是负数,难以用矩阵来存储坐标点,所以使用稀疏矩阵来存储。用STL的map来存储坐标是最为简单的。用C语言实现的话,要复杂很多,所以就不做题解了。
const int N = 1000;pair<int, int> p[N];const int CN = 4;int cnt[CN + 1];
int main(){ int n; map<pair<int, int>, int> ps; scanf("%d", &n);
for(int i = 0; i < n; i++) {int x, y; scanf("%d%d", &x, &y);
p[i] = make_pair(x, y); ps[p[i]] = 1; }
memset(cnt, 0, sizeof(cnt));
for(int i = 0; i < n; i++) { int x = p[i].first; int y = p[i].second;
if(ps[make_pair(x, y -1)] && ps[make_pair(x, y + 1)] &&
ps[make_pair(x - 1, y)] && ps[make_pair(x + 1, y)])
cnt[ps[make_pair(x - 1, y - 1)] + ps[make_pair(x - 1, y + 1)] +
ps[make_pair(x + 1, y - 1)] + ps[make_pair(x + 1, y + 1)]]++; }
for(int i = 0; i <= CN; i++) printf("%d\n", cnt[i]); return 0;}
201909-2 小明种苹果(续)(模拟)
int flag[N];
int main(void){ int n, m, a, i, j; int t = 0, d = 0, e = 0; scanf("%d", &n);
for(i = 0; i < n; i++) { int sum = 0; scanf("%d", &m); scanf("%d", &sum);
flag[i] = 0;
for(j = 2; j <= m; j++) { scanf("%d", &a);
if(a <= 0) sum += a;
else if(a < sum) {flag[i] = 1;sum = a; } }
t += sum;
if(flag[i]) d++; }
if(n >= 3)
for(i = 0; i < n; i++)
if(flag[(i - 1 + n) % n] && flag[i] && flag[(i + 1) % n]) e++;
printf("%d %d %d\n", t, d, e); return 0;}
201903-2 二十四点(表达式求值,stack)
C++中的除法是向零取整,去尾法取整
stack<int> num;stack<char> op;
void eval(){
int b = num.top(); num.pop(); int a = num.top(); num.pop();
char c = op.top(); op.pop(); int x;
if(c == '+') x = a + b;
else if(c == '-') x = a - b;
else if(c == 'x') x = a * b;
else //对于除法要特判一下,因为本题是向下取整,c++中默认是a / b向0取整 {
if(a * b >= 0) x = a / b; //a,b同号,结果一定为正,直接算
else //a, b异号 {
if(a % b == 0) x = a / b; //a, b能整除,则直接除
else x = a / b - 1; // a,b不能整数,向下取整 } }
num.push(x); //将答案入栈}
int main(){
unordered_map<char, int> pr{{'+', 1}, {'-', 1}, {'x', 2}, {'/', 2}}; //定义符号优先级
int n; cin >> n;
while(n -- ){ num = stack<int>(); op = stack<char>(); //每组数据要清空栈
string str; cin >> str;
for(int i = 0; i < str.size(); i ++ ) { char c = str[i];
if(isdigit(c)) //如果这个字符是数字,压入栈中 {
num.push(c - '0'); }
else //这个字符是运算符号 {
while(op.size() && pr[op.top()] >= pr[c]) eval(); //如果栈内有运算符且优先级大于等于当前运算符,则计算一下
op.push(c); //符号栈内无运算符或者栈内运算符优先级比当前运算符优先级低,直接入栈}}
while(op.size()) eval(); //最后如果符号栈里还有符号,则算完为止
if(num.top() == 24) puts("Yes"); else puts("No");}}
201812-2 小明放学(数学)
typedef long long LL;
int main(){ int r, y, g; cin >> r >> y >> g; int n; cin >> n; LL res = 0;
while (n -- ) { LL k, t; cin >> k >> t;
if (!k) res += t;
else {
if (k == 1) t = r - t;
else if (k == 2) t = r + y + g - t;
else t = r + g - t;
t += res; t %= r + y + g;
if (t < r) res += r - t;
else if (t >= r + g) res += r + g + y - t + r;}}
cout << res << endl;}
201809-2 买菜(两线段重叠计算)
#define x first#define y second
using namespace std;typedef pair<int, int> PII;const int N = 2010;int n;PII p[N], q[N];
int get(PII a, PII b){
if (a.y < b.x || b.y < a.x) return 0;
return min(a.y, b.y) - max(a.x, b.x);}
int main(){ cin >> n;
for (int i = 0; i < n; i ++ ) cin >> p[i].x >> p[i].y;
for (int i = 0; i < n; i ++ ) cin >> q[i].x >> q[i].y;
int res = 0;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n; j ++ )
res += get(p[i], q[j]);
cout << res << endl;}
201803-2 碰撞的小球(模拟)
const int N = 110;int n, l, t;pair<int, int> q[N];
int main(){ cin >> n >> l >> t;
for(int i = 0; i < n; i ++){ cin >> q[i].first; q[i].second = 1; }
while (t --){
for(int i = 0; i < n; i ++){ q[i].first += q[i].second;
//小球到达两端时返回,速度与原来相反...
if(q[i].first == 0 || q[i].first == l){ q[i].second *= -1; } }
for(int i = 0; i < n; i ++){ //必须得再起个循环,
if(a[i].first==a[i+1].first) a[i].second*=(-1),a[i+1].second*=(-1);}}
for(int i = 0; i < n; i ++) cout << q[i].first << " ";}
201709-2 公共钥匙盒(模拟)
int n, m;int q[N];
struct Op //把操作封装成结构体,然后给操作按先后顺序排序{
int tm, type, id; //type=0借,=1还
bool operator< (const Op& t) const{
if (tm != t.tm) return tm < t.tm;
if (type != t.type) return type > t.type;
return id < t.id; }
}op[N * 2];
int main(){ cin >> n >> m; int k = 0;
while (m -- ){ int id, start, len; cin >> id >> start >> len;
op[k ++ ] = {start, 0, id}; op[k ++ ] = {start + len, 1, id}; }
sort(op, op + k);
for (int i = 1; i <= n; i ++ ) q[i] = i;
for (int i = 0; i < k; i ++ ) { int id = op[i].id;
if (!op[i].type) //还钥匙 {
for (int j = 1; j <= n; j ++ )
if (q[j] == id) { q[j] = 0; break; } }
else//借钥匙 {
for (int j = 1; j <= n; j ++ )
if (!q[j]) {q[j] = id; break; }}}
for (int i = 1; i <= n; i ++ ) cout << q[i] << ' ';}
201703-2 学生排队(序列)
int n, m;int q[N];
int main(){ cin >> n >> m;
for (int i = 1; i <= n; i ++ ) q[i] = i;
while (m -- ) { int a, b; cin >> a >> b; int k;
for (int i = 1; i <= n; i ++ )
if (q[i] == a) k = i;
if (b > 0) {
for (int i = 0; i < b; i ++ )
swap(q[k + i], q[k + i + 1]);//后移 }
else { b = -b;
for (int i = 0; i < b; i ++ )
swap(q[k - i], q[k - i - 1]); //前移 } }
for (int i = 1; i <= n; i ++ ) cout << q[i] << ' '; return 0;}
201612-2 工资计算(数组,分段,方程)
int get(int x)//计算税后工资,设x正的做{
if (x <= 3500) return x;
int a[] = {0, 1500, 4500, 9000, 35000, 55000, 80000, 1000000};
int b[] = {0, 3, 10, 20, 25, 30, 35, 45}; //税率%
x -= 3500; int tax = 0;
for (int i = 1; i < 8; i ++ )
if (x >= a[i - 1]) tax += (min(x, a[i]) - a[i - 1]) / 100 * b[i];
return x + 3500 - tax;}
int main(){ int T; cin >> T;
for (int i = 0; ; i += 100)
if (get(i) == T) { cout << i << endl; break; } return 0;}
201609-2 火车购票(分类模拟)
// 座位排
struct Row{ int no; //排号
int remain; //剩余座位 };
int main(){ Row r[20]; int i,j,k;
for(i=0;i<20;i++){ r[i].no=i;r[i].remain=5; }
int n; cin>>n; int p[n];
for(i=0;i<n;i++){ cin>>p[i]; }
for(i=0;i<n;i++){
for(j=0;j<20;j++){
if(p[i]<=r[j].remain){//最小排号的剩余座位多于购买座位数 }
while(p[i]>0){
r[j].remain--; //剩余座位-1
cout<<(j+1)*5-r[j].remain<<" "; //输出购买座位号
p[i]--; }
cout<<endl;break;} }
if(j==20){ //购买的票无法安排在相邻座位
for(k=0;k<20;k++){
if(r[k].remain>0){//安排在编号最小的几个空座位中
while(p[i]>0 && r[k].remain>0){
r[k].remain--;cout<<(k+1)*5-r[k].remain<<" ";p[i]--;}
if(p[i]==0) break;}}}}}
201604-2 俄罗斯方块(模拟)
int g[N][N], s[N][N];int p[4][4];
bool draw(int x, int y){ memcpy(s, g, sizeof s);//把g为起始地址的连续n个空间拷贝到s
for (int i = 0; i < 4; i ++ )
for (int j = 0; j < 4; j ++ )
if (p[i][j]) { int a = x + i, b = y + j; s[a][b] ++ ;
if (s[a][b] == 2) return true; }
return false;}
int main(){
for (int i = 0; i < 15; i ++ )
for (int j = 0; j < 10; j ++ ) cin >> g[i][j];
for (int i = 0; i < 10; i ++ ) g[15][i] = 1;
for (int i = 0; i < 4; i ++ )
for (int j = 0; j < 4; j ++ ) cin >> p[i][j];
int c; cin >> c; c -- ;
for (int i = 0; ; i ++ )
if (draw(i, c)) { draw(i - 1, c); break; }
for (int i = 0; i < 15; i ++ ) {
for (int j = 0; j < 10; j ++ ) cout << s[i][j] << ' ';
cout << endl; } return 0;}
201512-2 消除类游戏(模拟)
const int N = 33;int n, m;int g[N][N];bool st[N][N];
int main(){ cin >> n >> m;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ ) cin >> g[i][j];
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ ){ int l = j, r = j, u = i, d = i, x = g[i][j];
while (l >= 0 && g[i][l] == x) l -- ;
while (r < m && g[i][r] == x) r ++ ;
while (u >= 0 && g[u][j] == x) u -- ;
while (d < n && g[d][j] == x) d ++ ;
st[i][j] = r - l - 1 >= 3 || d - u - 1 >= 3; }
for (int i = 0; i < n; i ++ ) {
for (int j = 0; j < m; j ++ )
if (st[i][j]) cout << 0 << ' ';
else cout << g[i][j] << ' ';
cout << endl; } return 0;}
201509-2 日期计算(分段,数组)
int months[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int is_leap(int year){
if (year % 4 == 0 && year % 100 || year % 400 == 0) return 1;
return 0;}
int get_days(int y, int m){ int s = months[m];
if (m == 2) return s + is_leap(y);
return s;}
int main(){ int year, d; cin >> year >> d;
for (int i = 1; i <= 12; i ++ )
for (int j = 1; j <= get_days(year, i); j ++ ) {
if ( -- d == 0) { printf("%d\n%d\n", i, j); return 0;}}}
201503-2 数字排序(序列)
int n;int cnt[N];
struct Data{ int v, c;
// 重载运算符“<”,括号中的const表示参数a对象不会被修改,最后的const表明调用函数对象不会被修改!
bool operator< (const Data& t) const {
if (c != t.c) return c > t.c;
return v < t.v; }
}q[N];
int main(){ cin >> n;
while (n -- ) { int x; cin >> x; cnt[x] ++ ; }
n = 0;
for (int i = 0; i < N; i ++ )
if (cnt[i]) q[n ++ ] = {i, cnt[i]};
sort(q, q + n);
for (int i = 0; i < n; i ++ ) cout << q[i].v << ' ' << q[i].c << endl;}
201412-2 Z字形扫描(二维)
扩展格子,只输出范围内数字,斜的为一行
int n;int q[N][N];
int main(){ scanf("%d", &n);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ ) scanf("%d", &q[i][j]);
for (int i = 2; i <= n * 2; i ++ )
if (i % 2 == 0) {
for (int j = i - 1; j; j -- )
if (j >= 1 && j <= n && i - j >= 1 && i - j <= n)
printf("%d ", q[j][i - j]); }
else {
for (int j = 1; j < i; j ++ )
if (j >= 1 && j <= n && i - j >= 1 && i - j <= n)
printf("%d ", q[j][i - j]);}}
201409-2 画图(二维)
int n;bool st[N][N];//涂色格子
int main(){ cin >> n;
while (n -- ) { int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
for (int i = x1; i < x2; i ++ )
for (int j = y1; j < y2; j ++ ) st[i][j] = true; }
int res = 0;
for (int i = 0; i < N; i ++ )
for (int j = 0; j < N; j ++ ) res += st[i][j];
cout << res << endl; return 0;}
201403-2 窗口(模拟)
int n, m;
struct Window{ int x1, y1, x2, y2; int id;}w[N];
int get(int x, int y){
for (int i = n; i; i -- )
if (x >= w[i].x1 && x <= w[i].x2 && y >= w[i].y1 && y <= w[i].y2) return i;
return 0;}
int main(){ cin >> n >> m;
for (int i = 1; i <= n; i ++ ) { int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
w[i] = {x1, y1, x2, y2, i}; }
while (m -- ) { int x, y; cin >> x >> y; int t = get(x, y);
if (!t) puts("IGNORED");
else { cout << w[t].id << endl;
struct Window r = w[t];//auto c++11
for (int i = t; i < n; i ++ ) w[i] = w[i + 1];
w[n] = r;}}}
201312-2 ISBN号码 (模拟题,字符串)
int main(){ string str; cin>>str;
int sum=0;
for(int i=0,j=1;i+1<str.size();i++)//卡到最后一位
if(str[i]!='-'){sum+=(str[i]-'0')*j; j++; }
sum%=11; char c=sum+'0';
if(sum==10) c='X';
if(str.back()==c) puts("Right");
else{ str.back()==c; cout<<str<<endl;}}
201312-3 最大的矩形(分类)
int n,h[N];
int main(){ cin>>n;
for(int i=1;i<=n;i++) cin>>h[i];
int res=0;
for(int i=1;i<=n;i++){ int l=i;r=i;
while(l>=1&&h[l]>=h[i]) l--;
while(r<=n&&h[r]>=h[i]) r++;
res=max(res,h[i]*(r-l-1)); }
cout<<res<<endl;}
搜索与图论
dfs
201709-4 通信网络 (dfs)
const int N = 1010,M = 20010;int n, m,h1[N],h2[N],e[M],ne[M],idx;bool st1[N], st2[N];
void add(int h[], int a, int b){e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;}
void dfs(int u, int h[], bool st[]){ st[u] = true;//当前点能到
for (int i = h[u]; ~i; i = ne[i]){ int j = e[i];
if (!st[j]) dfs(j, h, st);}}
int main(){ cin >> n >> m; memset(h1, -1, sizeof h1); memset(h2, -1, sizeof h2);
while (m -- ){int a, b;cin >> a >> b;
add(h1, a, b), add(h2, b, a);}
int res = 0;
for (int i = 1; i <= n; i ++ ){memset(st1, 0, sizeof st1);memset(st2, 0, sizeof st2);
dfs(i, h1, st1);dfs(i, h2, st2);
int s = 0;
for (int j = 1; j <= n; j ++ )
if (st1[j] || st2[j])s ++ ;
if (s == n) res ++ ;}
cout << res << endl;}
bfs
拓扑
int h[N],e[N],ne[N],idx; int q[N],hh,tt=-1,n,m; int deg[N];
void add(int a,int b){e[idx]=b;ne[idx]=h[a];h[a]=idx++;}
bool bfs(){
for(int i=1;i<=n;i++){
if(!deg[i]){ q[++tt]=i;} }
while(hh<=tt){ int t=q[hh++];
for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];//找到出边 deg[j]--;
if(deg[j]==0) q[++tt]=j;}}
return tt==n-1;}
int main(){scanf("%d%d",&n,&m);memset(h,-1,sizeof(h));
while(m--){int x,y;cin>>x>>y;deg[y]++;add(x,y); }
if(bfs()){ //拓扑
for(int i=0;i<n;i++){cout<<q[i]<<" ";}
}else{cout<<-1;}}
201604-4 游戏(bfs)
const int N = 110, M = 310, INF = 0x3f3f3f3f;int n, m, T,dist[N][N][M];bool g[N][N][M];
struct Node{
int x, y, t;
}q[N * N * M];
int bfs(){int hh = 0, tt = 0; q[0] = {1, 1, 0};
memset(dist, 0x3f, sizeof dist);
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
while (hh <= tt){ Node t = q[hh ++ ];
for (int i = 0; i < 4; i ++ ){ int x = t.x + dx[i], y = t.y + dy[i];
if (x < 1 || x > n || y < 1 || y > m) continue;
if (g[x][y][t.t + 1]) continue;
if (dist[x][y][t.t + 1] > t.t + 1){
if (x == n && y == m) return t.t + 1;
dist[x][y][t.t + 1] = t.t + 1;
q[ ++ tt] = {x, y, t.t + 1};}}}
return 0;}
int main(){ cin >> n >> m >> T;
while (T -- ){int r, c, a, b; cin >> r >> c >> a >> b;
for (int i = a; i <= b; i ++ ) g[r][c][i] = true;}
cout << bfs() << endl;}
最短路
一、朴素Dijkstra
int g[N][N]; // 存储每条边
int dist[N]; // 存储1号点到每个点的最短距离
bool st[N]; // 存储每个点的最短路是否已经确定
// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra(){ memset(dist, 0x3f, sizeof dist); dist[1] = 0;
for (int i = 0; i < n - 1; i ++ ) {
int t = -1; // 在还未确定最短路的点中,寻找距离最小的点
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))t = j; // 用t更新其他点的距离
for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], dist[t] + g[t][j]);
st[t] = true;}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];}
二、堆优化的dijkstra
typedef pair<int, int> PII;
int n; // 点的数量
int h[N], w[N], e[N], ne[N], idx; // 邻接表存储所有边
int dist[N]; // 存储所有点到1号点的距离
bool st[N]; // 存储每个点的最短距离是否已确定
// 求1号点到n号点的最短距离,如果不存在,则返回-1
int dijkstra(){memset(dist, 0x3f, sizeof dist); dist[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, 1}); // first存储距离,second存储节点编号
while (heap.size()){ auto t = heap.top();heap.pop();
int ver = t.second, distance = t.first;
if (st[ver]) continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i]){int j = e[i];
if (dist[j] > distance + w[i]){dist[j] = distance + w[i];
heap.push({dist[j], j});}}}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];}
三、Bellman-Ford
int n, m; // n表示点数,m表示边数
int dist[N]; // dist[x]存储1到x的最短路距离
struct Edge // 边,a表示出点,b表示入点,w表示边的权重{
int a, b, w;
}edges[M];
// 求1到n的最短路距离,如果无法从1走到n,则返回-1。
int bellman_ford(){memset(dist, 0x3f, sizeof dist);dist[1] = 0;
for (int i = 0; i < n; i ++ ){
for (int j = 0; j < m; j ++ ){int a = edges[j].a, b = edges[j].b, w = edges[j].w;
if (dist[b] > dist[a] + w)dist[b] = dist[a] + w;}}
if (dist[n] > 0x3f3f3f3f / 2) return -1;
return dist[n];}
四、spfa
int n; // 总点数
int h[N], w[N], e[N], ne[N], idx; // 邻接表存储所有边
int dist[N]; // 存储每个点到1号点的最短距离
bool st[N]; // 存储每个点是否在队列中
// 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1
int spfa(){memset(dist, 0x3f, sizeof dist);dist[1] = 0;queue<int> q;
q.push(1);st[1] = true;
while (q.size()){auto t = q.front();q.pop();st[t] = false;
for (int i = h[t]; i != -1; i = ne[i]){int j = e[i];
if (dist[j] > dist[t] + w[i]){dist[j] = dist[t] + w[i];
if (!st[j]){q.push(j);st[j] = true;} }}}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];}
五、Floyd
const int N=210, inf=0x3f3f3f3f;int n,m,q;int d[N][N];
void floyd(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);}}}}
int main(){scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) d[i][j]=0;else d[i][j]=inf;}}
while(m--){int x,y,z;scanf("%d%d%d",&x,&y,&z);d[x][y]=min(d[x][y],z);//注意重边}
floyd();
while(q--){int x,y;scanf("%d%d",&x,&y);
if(d[x][y]>inf/2) puts("impossible");else printf("%d\n",d[x][y]);}}
201809-4 再卖菜(spfa 差分约束)
const int N = 310, M = N * 3;
int n;int h[N], e[M], w[M], ne[M], idx;int dist[N], q[N];int b[N];bool st[N];
void add(int a, int b, int c){e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;}
void spfa(){int hh = 0, tt = 1;memset(dist, -0x3f, sizeof dist);
dist[0] = 0;q[0] = 0;
while (hh != tt){int t = q[hh ++ ];
if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i]){int j = e[i];
if (dist[j] < dist[t] + w[i]){dist[j] = dist[t] + w[i];
if (!st[j]){ q[tt ++ ] = j;
if (tt == N) tt = 0;
st[j] = true;}}}}}
int main(){cin >> n;memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ ) cin >> b[i];
for (int i = 2; i < n; i ++ ){
add(i - 2, i + 1, b[i] * 3); add(i + 1, i - 2, -(b[i] * 3 + 2));}
add(0, 2, b[1] * 2), add(2, 0, -(b[1] * 2 + 1));
add(n - 2, n, b[n] * 2), add(n, n - 2, -(b[n] * 2 + 1));
for (int i = 1; i <= n; i ++ ) add(i - 1, i, 1);
spfa();
for (int i = 1; i <= n; i ++ )
cout << dist[i] - dist[i - 1] << ' ';}
201712-4 行车路线 (Dijkstra)
const int N = 510, M = 200010;const int INF = 1e6;
int n, m;int h[N], e[M], ne[M], w[M], idx;int f[M];// 边的类型,大路还是小路
bool st[N][1010];int dist[N][1010];//第二维为拆点
struct Node{
// 含义:点的编号,最后一段小路的长度,1到x点的最短距离
int x, y, v;
// 小根堆(距离从小到大)
bool operator<(const Node& t)const{
return v > t.v;
}
};
void add(int t, int a, int b, int c){
e[idx] =b ,w[idx] = c, f[idx] = t,ne[idx] = h[a], h[a] = idx ++;}
void dijkstra(){ memset(dist, 0x3f, sizeof dist);priority_queue<Node> heap;
heap.push({1, 0, 0});dist[1][0] = 0;
while(heap.size()){auto t = heap.top();heap.pop();
if(st[t.x][t.y]) continue;
st[t.x][t.y] = true;
for(int i = h[t.x]; ~i; i= ne[i]){nt x = e[i], y = t.y;//最后一段小路的长度y
if(f[i]){ y += w[i]; //小路长度更新
if(y <= 1000){ // 小路长度不会超过1000
if(dist[x][y] > t.v - t.y * t.y + y * y){
// 更新1到x点的最短路
dist[x][y] = t.v - t.y * t.y + y * y;
if(dist[x][y] <= INF){// 加入堆
heap.push({x, y, dist[x][y]});}}}}
else{ // 大路
if(dist[x][0] > t.v + w[i]){dist[x][0] = t.v + w[i];//权值累加
if(dist[x][0] <= INF)heap.push({x,0, dist[x][0]});}}}}}
int main(){cin >> n >> m;memset(h, -1, sizeof h);
while(m --){int t, a, b, c;cin >> t >> a >> b >> c;add(t, a, b, c), add(t, b, a, c);}
dijkstra();
int res = INF;
// dist[n][i] 表示最后一段小路的长度为i的前提下,1到n的最短路
// 我们通过dijkstra算法求得了合法的、最后一段是小路、但是小路长度不同的所有情况
// 取min即可
for(int i = 0; i <= 1000; i ++) res = min(res, dist[n][i]);
cout << res << endl;}
201609-4 交通规划(堆优化Dijkstra/spfa)
最短路径树(堆优化Dijkstra)单源最短路
typedef pair<int, int> PII;const int N = 10010, M = 2e5 + 10, INF = 0x3f3f3f3f;
int n, m;int h[N], w[M], e[M], ne[M], idx;int dist[N];bool st[N];
void add(int a, int b, int c){e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;}
void dijkstra(){memset(dist, 0x3f, sizeof dist);dist[1] = 0; //1号点的距离为0
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, 1}); //first为距离,second为结点编号
while(heap.size()){PII t = heap.top();heap.pop();
int ver = t.second, distance = t.first;
if(st[ver]) continue;
st[ver] = true;
for(int i = h[ver]; i != -1; i = ne[i]){int j = e[i];
if(dist[j] > distance + w[i]){dist[j] = distance + w[i];
heap.push({dist[j], j});}}}}
int main(){scanf("%d%d", &n, &m);memset(h, -1, sizeof h);
while(m -- ){int a, b, c;scanf("%d%d%d", &a, &b, &c);add(a, b, c), add(b, a, c);}
dijkstra();
//求每个点满足条件的邻边(每个点的满足条件的边中权值最小的边就是我们要找的边)
int res = 0;
for(int a = 2; a <= n; a ++ ){ int minw = INF; //用minw表示权值最小的边
for(int j = h[a]; ~j; j = ne[j]){int b = e[j];
if(dist[a] == dist[b] + w[j]){minw = min(minw, w[j]);}}
res += minw;}
printf("%d\n", res);}
201409-4 最优配餐(bfs 多源最短路)
#define x first #define y second
using namespace std;typedef long long LL;typedef pair<int, int> PII;const int N = 1010;
int n, m, k, d;bool g[N][N];int dist[N][N];queue<PII> q;
struct Target{
int x, y, c;
}tg[N * N];
void bfs(){ int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
while (q.size()){ auto t = q.front();q.pop();
for (int i = 0; i < 4; i ++ ){int x = t.x + dx[i], y = t.y + dy[i];
if (x < 1 || x > n || y < 1 || y > n || g[x][y]) continue;
if (dist[x][y] > dist[t.x][t.y] + 1){dist[x][y] = dist[t.x][t.y] + 1;
q.push({x, y});}}}}
int main(){scanf("%d%d%d%d", &n, &m, &k, &d);memset(dist, 0x3f, sizeof dist);
while (m -- ){int x, y;scanf("%d%d", &x, &y); dist[x][y] = 0;q.push({x, y}); }
for (int i = 0; i < k; i ++ ) scanf("%d%d%d", &tg[i].x, &tg[i].y, &tg[i].c);
while (d -- ){int x, y;scanf("%d%d", &x, &y);g[x][y] = true;}bfs();
LL res = 0;
for (int i = 0; i < k; i ++ )
res += dist[tg[i].x][tg[i].y] * tg[i].c;
printf("%lld\n", res);}
201403-4 无线网络 (SPFA)
最小生成树(无向图)
一、Prim
/*S:当前已经在联通块中的所有点的集合
1. dist[i] = inf
2. for n 次
t<-S外离S最近的点
利用t更新S外点到S的距离
st[t] = true
n次迭代之后所有点都已加入到S中
联系:Dijkstra算法是更新到起始点的距离,Prim是更新到集合S的距离
*/
const int N=510,INF=0x3f3f3f3f,n,m,g[N][N],dist[N];//邻接矩阵存储所有边,dist存储其他点到S的距离
bool st[N];
int prim() {//如果图不连通返回INF, 否则返回res
memset(dist, INF, sizeof dist);int res = 0;
for(int i = 0; i < n; i++) {int t = -1;
for(int j = 1; j <= n; j++)
if(!st[j] && (t == -1 || dist[t] > dist[j]))t = j;
if(i && dist[t] == INF) return INF; //寻找离集合S最近的点,dist[t]当前点
if(i) res += dist[t];//判断是否连通,有无最小生成树
//cout << i << ' ' << res << endl;
st[t] = true;//已加入,更新最新S的权值和
for(int j = 1; j <= n; j++) dist[j] = min(dist[j], g[t][j]);}
return res;}
int main() {cin >> n >> m;int u, v, w;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(i ==j) g[i][j] = 0;else g[i][j] = INF;
while(m--) {cin >> u >> v >> w;g[u][v] = g[v][u] = min(g[u][v], w);}
int t = prim();//临时存储防止执行两次函数导致最后仅返回0
if(t == INF) puts("impossible");
else cout << t << endl;}
二、Kruskal
const int N=2e5+5;int n,m;int p[N];
struct Edge{int u,v,w;
bool operator<(const Edge &a) const{ return w<a.w;}
}edge[N];
int find(int x){return p[x]==x?x:p[x]=find(p[x]);}
int main(){int n,m;scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){int u,v,w;scanf("%d%d%d",&u,&v,&w);edge[i]={u,v,w};}
sort(edge,edge+m);
for(int i=1;i<=n;i++) p[i]=i;
int cnt=0,sum=0;
for(int i=0;i<m;i++){int a=edge[i].u,b=edge[i].v,w=edge[i].w;
a=find(a);b=find(b);
if(a!=b){cnt++;sum+=w;p[a]=b;}}
if(cnt<n-1) puts("impossible");
else printf("%d",sum);}
201812-4 数据中心(Kruskal)
const int N = 50010, M = 100010;int n, m;int p[N];
struct Edge{int a, b, c;
bool operator< (const Edge& t) const{ return c < t.c;}
}edge[M];
int find(int x){if (p[x] != x) p[x] = find(p[x]); return p[x];}
int main(){ scanf("%d%d%*d", &n, &m);
for (int i = 1; i <= n; i ++ ) p[i] = i;
for (int i = 0; i < m; i ++ ){int a,b,c;scanf("%d%d%d", &a, &b, &c);
edge[i] = {a, b, c};}
sort(edge, edge + m);int res = 0;
for (int i = 0; i < m; i ++ ){int a = edge[i].a, b = edge[i].b, c = edge[i].c;
if (find(a) != find(b)){p[find(a)] = find(b);res = c;}}
printf("%d\n", res);}
201412-4 最优灌溉 (Kruskal)
int n, m;int p[N];
struct Edge{int a, b, c;
bool operator< (const Edge& t) const{ return c < t.c;}
}e[N];
int find(int x){if (p[x] != x) p[x] = find(p[x]);return p[x];}
int main(){ scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) p[i] = i;
for (int i = 0; i < m; i ++ ){int a, b, c;scanf("%d%d%d", &a, &b, &c);
e[i] = {a, b, c};}
sort(e, e + m);int res = 0;
for (int i = 0; i < m; i ++ ){int a = e[i].a, b = e[i].b, c = e[i].c;
if (find(a) != find(b)){res += c;p[find(a)] = find(b);}}
printf("%d\n", res);}
201703-4 地铁修建 (Kruskal)
其它
201912-4 区块链(图的模拟)
201803-4 棋局评估 (博弈论中的对抗搜索)
201512-4 送货(欧拉回路)
const int N = 10010, M = 100010;int n, m;set<int> g[N];int p[N];int ans[M], top;
int find(int x){if (p[x] != x) p[x] = find(p[x]); return p[x];}
void dfs(int u){
while (g[u].size()){int t = *g[u].begin(); g[u].erase(t), g[t].erase(u);dfs(t); }
ans[ ++ top] = u;}
int main(){scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) p[i] = i;
while (m -- ){int a, b; scanf("%d%d", &a, &b);
g[a].insert(b), g[b].insert(a);p[find(a)] = find(b); }
int s = 0;
for (int i = 1; i <= n; i ++ )
if (find(i) != find(1)){ puts("-1");return 0; }
else if (g[i].size() % 2) s ++ ;
if (s != 0 && s != 2 || s == 2 && g[1].size() % 2 == 0){puts("-1");return 0; }
dfs(1);
for (int i = top; i; i -- )
printf("%d ", ans[i]);}
201509-4 高速公路(有向图强联通分量的个数)
const int N = 10010, M = 50010;
int n, m;int h[N], w[M], e[M], ne[M], idx; // 邻接表一套
// dfn[u] 存的是遍历到u的时间戳
// low[u]存的是从u出发,遍历子树,所能遍历到的最小的时间戳
//timestamp 就是时间戳
int dfn[N], low[N], timestamp;
int stk[N], top; // 栈,和栈顶元素索引
bool in_stk[N]; // 是否在栈中
//id[u]表示u的强连通分量的编号,scc_cnt表示强连通分量的编号
// size_scc[u]表示编号为u强连通分量中点的数量
int id[N], scc_cnt, size_scc[N];
int dout[N];// 记录新图中每个点(也就是原图每个连通分量)的出度
void add(int a, int b){ e[idx] = b, ne[idx] = h[a], h[a] = idx ++;}
void tarjan(int u){ dfn[u] = low[u] = ++ timestamp;//当前点的时间戳
stk[++ top] = u, in_stk[u] = true; // 加入栈中
for(int i = h[u]; ~i; i = ne[i]){ //遍历u点的所有邻点
int j = e[i];
if(!dfn[j]){//如果没有遍历过
tarjan(j); // 遍历它
low[u] = min(low[u], low[j]);}
// 当前点在栈当中
else if(in_stk[j]) low[u] = min(low[u], dfn[j]);}
if(dfn[u] == low[u]){
++ scc_cnt; // 更新强连通分量的编号
int y;
do{
y = stk[ top--]; //不断取出栈内元素
in_stk[y] = false;
id[y] = scc_cnt; //y元素所属的连通块编号
size_scc[scc_cnt] ++; //该连通块内包含的点数
}while(y != u); // 直到y不等于u }}
int main(){cin >> n >> m; memset(h, -1, sizeof h);
while(m --){int a, b;cin >> a >> b;add(a, b);}
for(int i = 1; i <= n; i ++){
if(!dfn[i])tarjan(i); }
// 建有向无环图
// 统计在新图中所有点的出度
for(int i = 1; i <= n; i ++){
for(int j = h[i]; ~j; j = ne[j]){
int k = e[j];
int a = id[i]; //a表示i所在连通分量的编号
int b = id[k]; // b表示k所在连通分量的编号
//如果点i和点k不在同一个连通块
// dout存的是出度,因为本题只需要出度
//在其他题目中,可能是要建边,因为这里是构造有向无环图
if(a != b) dout[a] ++; // 从a走到b,a的出度++ }}
// 和本题有关的部分:
// zeros是统计在新图中,出度为0的点的个数
// sum表示满足条件的点(最受欢迎的奶牛)的个数
int zeros = 0, sum = 0;
for(int i = 1; i <= scc_cnt; i ++){
if(!dout[i]){zeros ++; sum += size_scc[i];
if(zeros > 1){sum = 0; break;}}}
cout << sum << endl;}