ccf分题型刷题(c++)

代码格式乱了😅,
刷题经验:
第一题:一层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;}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值