zoukankan      html  css  js  c++  java
  • 7.22


    好几天了,一定要进校队!


    7.21场地址
    A题:
    题意:类比竖式加法,每个字母都要分配一个数字,最后一行是结果,之前多行是因数,求一共存在多少种字母的分配方案
    解法:枚举每一个字母,每枚举一个检查一遍

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <stack>
    #include <queue>
    #include <map>
    #include <set>
    #include <vector>
    #include <math.h>
    #include <bitset>
    #include <algorithm>
    #include <climits>
    using namespace std;
    
    #define ls 2*i
    #define rs 2*i+1
    #define UP(i,x,y) for(i=x;i<=y;i++)
    #define DOWN(i,x,y) for(i=x;i>=y;i--)
    #define MEM(a,x) memset(a,x,sizeof(a))
    #define W(a) while(a)
    #define gcd(a,b) __gcd(a,b)
    #define LL long long
    #define N 20005
    #define INF 0x3f3f3f3f
    #define EXP 1e-8
    #define rank rank1
    const int mod = 1000000007;
    
    int n, letter[20], cot, len[20], hsh[200], num[200], ans;
    char str[20][20];
    
    int check(){
        int i, j, k;
        for (i = 0; i<n; i++)//前导0不行
        {
            if (num[str[i][0]] == 0)
                return 0;
        }
        int jin = 0;//进位
        for (j = 0; j<len[n - 1]; j++)//从右边(个位)开始算
        {
            if (num[str[n - 1][len[n - 1] - 1 - j]] == -1) return 1;//这个字母没有被赋值,返回并往下搜
            int sum = jin;
            for (i = 0; i<n - 1; i++){
                if (len[i] - 1 - j<0) continue;
                if (num[str[i][len[i] - 1 - j]] == -1) return 1;//这个字母没有被赋值,返回并往下搜
                sum += num[str[i][len[i] - 1 - j]];
            }
            if (sum % 10 != num[str[n - 1][len[n - 1] - 1 - j]]) return 0;//对应位之和与最后一个不等
            jin = sum / 10;
        }
        return !jin;//进位为0则解决了
    }
    
    void dfs(int cnt)
    {
        int i;
        if (cnt == -1)
        {
            ans++;
            return;
        }
        for (i = 0; i<10; i++)//给字母尝试所有不同的赋值赋值
        {
            if (hsh[i] == 0)
            {
                hsh[i] = 1;
                num[letter[cnt]] = i;
                if (check())//赋值可行则继续进行
                    dfs(cnt - 1);
                hsh[i] = 0;
                num[letter[cnt]] = -1;
            }
        }
    }
    
    int main()
    {
        int i, j, k;
        while (~scanf("%d", &n))
        {
            MEM(hsh, 0);
            cot = -1;
            for (i = 0; i < n; i++)
            {
                scanf("%s", str[i]);
                len[i] = strlen(str[i]);
                for (j = 0; j < len[i]; j++)
                {
                    if (!hsh[str[i][j]])
                    {
                        hsh[str[i][j]] = 1;
                        letter[++cot] = str[i][j];//记录所有字母
                    }
                }
            }
            ans = 0;
            MEM(hsh, 0);
            MEM(num, -1);
            dfs(cot);
            printf("%d
    ", ans);
        }
        return 0;
    }
    

    B题:
    题意:给定起点和终点,统计所有最短路的长度和为多少
    解法:首先(最短路*条数)的方法是行不通的,检验一条路是不是最短路的方法为:
    最短路的起点到这条路的的起点的距离+最短路的终点到这条路终点的距离+这条路的距离=最短路。
    从起点和终点跑两边spfa,之后枚举每一条边即可

    #include<cstdio>
    #include<vector>
    #include<queue>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int maxn = 10005;
    const int maxm = 600000;
    const int INF = 0x3f3f3f3f;
    int n, m;
    struct ee {
        int to;
        int nxt;
        int w;
    }edge[maxm];
    int head[maxn], tot;
    void add_edge(int u, int v, int w) {
        edge[tot].to = v;
        edge[tot].w = w;
        edge[tot].nxt = head[u];
        head[u] = tot++;
    }
    int vis1[maxn], vis2[maxn];
    int dis1[maxn], dis2[maxn];
    
    void spfa1(int s, int t)
    {
        for (int i = 0; i < n; ++i) dis1[i] = INF;
        memset(vis1, false, sizeof( vis1));
        queue<int>q;
        q.push(s);
        dis1[s] = 0;
        vis1[s] = true;
        while (!q.empty()) {
            int u = q.front(); q.pop();
            vis1[u] = false;
            for (int i = head[u]; ~i; i = edge[i].nxt) {
                int &v = edge[i].to;
                int &cost = edge[i].w;
                if (dis1[v] > dis1[u] + cost) {
                    dis1[v] = dis1[u] + cost;
                    if (!vis1[v]) {
                        vis1[v] = true;
                        q.push(v);
                    }
                }
            }
        }
    }
    
    void spfa2(int s, int t)
    {
        for (int i = 0; i<n; ++i) dis2[i] = INF;
        memset(vis2, false, sizeof (vis2));
        queue<int> q;
        q.push(s);
        dis2[s] = 0;
        vis2[s] = true;
        while (!q.empty()) {
            int u = q.front(); q.pop();
            vis2[u] = false;
            for (int i = head[u]; ~i; i = edge[i].nxt) {
                int &v = edge[i].to;
                int &cost = edge[i].w;
                if (dis2[v] > dis2[u] + cost) {
                    dis2[v] = dis2[u] + cost;
                    if (!vis2[v]) {
                        vis2[v] = true;
                        q.push(v);
                    }
                }
            }
        }
    }
    
    int main() {
        scanf("%d%d", &n, &m);
        memset(head, -1, sizeof head);
        tot = 0;
        for (int i = 1; i <= m; i++) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            add_edge(u, v, w);
            add_edge(v, u, w);
        }
        spfa1(0, n - 1); spfa2(n - 1, 0);
        ll ans = 0;
        int len = dis1[n-1];
        for (int u = 0; u<n; u++){
            for (int i = head[u]; ~i; i = edge[i].nxt){
                if (dis1[u] + dis2[edge[i].to] + edge[i].w == len)
                    ans += edge[i].w;
            }
        }
        printf("%I64d
    ", ans * 2);
        return 0;
    }

    Uva1471
    题意:给定一个长度为n的序列,你的任务是删除一个连续子序列,使得剩下的序列中有一个长度最大的连续上升子序列
    解法:首先处理出从以i为终点的上升子序列长度,再处理出以i为起点的最长上升子序列长度,
    之后我们枚举每个i,在有序表中一次性找到他前面的j,使得a[j]

    #include<cstdio>
    #include<set>
    #include<algorithm>
    #include<iostream>
    #include<cassert>
    using namespace std;
    const int maxn = 2e5 + 5;
    int a[maxn], pre[maxn], nex[maxn];
    struct node {
        int a, pre;
        node(int a, int pre) :a(a), pre(pre){}
        bool operator < (const node& rhs) const { return a < rhs.a; }
    };
    set<node>s;
    
    //核心思想:处理出来pre[i]和nex[i]之后,我们就要枚举每个i,之后一次找到在i之前的
    //j的位置,用j的pre加上i的nex,用这个值来更新ans;
    //由于我们需要保证每次查找的j都是a[j]的值要最小且j的pre尽量长,所以每次比较完之后需要更新集合
    //插入任何一个二元组是先找到插入位置,根据前一个元素判断是否需要保留,如果需要保留,就往后保留
    //紫书:P242
    
    int main(){
        int T; cin >> T;
        while (T--) {
            int n; cin >> n;
            for (int i = 0; i < n; i++)
                cin >> a[i];
            if (n == 1) { cout << 1 << endl; continue; }
    
            pre[0] = 1;//以第i个元素结尾最长长度
            for (int i = 1; i < n; i++) {
                if (a[i - 1] < a[i])pre[i] = pre[i - 1] + 1;
                else pre[i] = 1;
            }
            nex[n-1] = 1;//以第i个元素开头的最长长度
            for (int i = n - 2; i > 0; i--) {
                if (a[i] < a[i + 1])nex[i] = nex[i + 1] + 1;
                else nex[i] = 1;
            }
    
            int ans = 1;
            s.clear(); s.insert(node(a[0], pre[0]));
            for (int i = 1; i < n; i++) {
                node now = node(a[i], pre[i]);
                set<node>::iterator it = s.lower_bound(now);
                bool keep = true;
                if (it != s.begin()) {
                    node last = *(--it);
                    int len = nex[i] + last.pre;
                    ans = max(ans, len);
                    if (last.pre >= now.pre) keep = false;
                }
                if (keep) {
                    s.erase(now); s.insert(now);//erase默认没找到就不删除
                    it = s.find(now); it++;
                    //更新后面的
                    while (it != s.end() && it->a > now.a && it->pre <= now.pre) { s.erase(it++); }
                }
            }
            cout << ans << endl;
        }
        return 0;
    }
    

    Uva714
    题意:把一个包含m个正整数的序列分成k个非空的连续子序列,使得每个正整数恰好属于一个序列,设第i个序列的个数之和s(i),找到分配方案,使得所有s(i)的最大值尽量小。
    解法:二分最小值x,二分的范围是元素最大值到所有元素的和

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    int p[10000], m, k;
    
    int solve(ll maxp) {
        int ans = 1;
        ll done = 0;
        for (int i = 0; i < m; i++) {
            if (done + p[i] <= maxp) { done += p[i]; }
            else { ans++; done = p[i]; }
        }
        return ans;
    }
    
    int last[1000];
    void print(ll ans) {
        memset(last, 0, sizeof(last));
        ll done = 0;
        int remain = k;
        for (int i = m - 1; i >= 0; i--) {
            if (done + p[i] > ans || i + 1<remain) {
                last[i] = 1; remain--; done = p[i];
            }
            else done += p[i];
        }
        for (int i = 0; i < m-1; i++) {
            cout << p[i] << " ";
            if (last[i])cout << "/ ";
        }
        cout <<p[m-1]<< endl;
        return;
    }
    
    int main() {
        int T; cin >> T;
        while (T--) {
            cin >> m >> k;
            ll tot;
            int maxp;
            tot = 0, maxp = -1;
            for (int i = 0; i < m; i++) {
                cin >> p[i];
                tot += p[i];
                maxp = max(maxp, p[i]);
            }
            ll l = maxp, r = tot;
            while (l < r) {
                ll mid = l + (r - l) / 2;
                if (solve(mid) <= k)r = mid;
                else l = mid + 1;
            }
            print(l);
        }
        return 0;
  • 相关阅读:
    【数学建模】—优秀论文(一)
    【数学建模】—论文排版
    【Linux学习】—第8章linux编程
    【Linux学习】—文件权限和目录配置
    【ESP8266学习】(一)
    【OpenCV】——b站达尔闻
    【Linux学习】——Shell编程基础
    【数学建模】——模拟退火算法(SAA)
    react 开发中火狐,Safari浏览器嵌套iframe显示空白
    element ui dataPicker 日期范围限制
  • 原文地址:https://www.cnblogs.com/romaLzhih/p/9489805.html
Copyright © 2011-2022 走看看