POJ - 1860 最短路 判正环

题意

一开始,在点有个金币,而点的过程会产生的变化,问是否有一条路线可以使得最终的

分析

要是最找的增加,有两种可能性:

  • 原本路径中就纯在这样的边权
  • 有正环的存在,因为而双向的通路,而若纯在一个正环,在环中走一些,不断的松弛操作后,最终会变成一个无穷大的数字,这样无论环中是否存在点都可以通过回路达到

所以本题能够利用的思想去解题。 因此初始化而源点到其他点的距离(权值)初始化为无穷小(0),当到其他某点的距离能不断变大时,说明存在最大路径;如果可以一直变大(也就是松弛次数超过次),说明存在正环。

本题因为数据范围不大,使用都可以。我此题采用的是。复杂度,SPFA的玄学常数,不过本题数据小无所谓。

AC代码

//#include <bits/stdc++.h>
#include <iostream>
#include <iomanip>
#include <bitset>
#include <string>
#include <set>
#include <stack>
#include <sstream>
#include <list>
//#include <array>
#include <vector>
#include <queue>
#include <map>
#include <memory>
#include <iterator>
#include <climits>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define all(x) (x).begin(), (x).end()
typedef long long ll;
typedef unsigned int UINT;
typedef unsigned long long ull;
typedef pair<int, int> pdi;
typedef pair<ll, int> pli;

int const maxn = 1e4 + 10;
const int INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
inline int lc(int x)  {return x << 1;}
inline int rc(int x)  {return x << 1 | 1;}

int vis[maxn];
int head[maxn], cnt, ans[maxn];
double dis[maxn];
int n, m, S;
double V;

struct node {
    int v, next;
    double r, c;
}e[maxn << 1];

void init() {
    memset(head, -1, sizeof(head));
    cnt = 0;
}

void add(int u, int v, double r, double c) {
    e[cnt].v = v;
    e[cnt].r = r, e[cnt].c = c;
    e[cnt].next = head[u];
    head[u] = cnt++;
}

bool SPFA(int s) {
    memset(vis, 0, sizeof(vis));
    memset(ans, 0, sizeof(ans));
    memset(dis, 0, sizeof(dis));

    queue<int> q;
    q.push(s);
    dis[s] = V;
    vis[s] = 1;
    ans[s] ++;
    while (!q.empty()) {
        int u = q.front();
        vis[u] = 0;
        q.pop();
        for (int i = head[u]; ~i; i = e[i].next) {
            int v = e[i].v;
            double r = e[i].r;
            double c = e[i].c;
            double w = (dis[u] - c) * r;
            if (dis[v] < w) {
                dis[v] = w;
                if (!vis[v]) {
                    vis[v] = 1;
                    q.push(v);
                    if (++ans[v] >= n) return true;
                }
            }
        }
    }

    if (dis[s] > V) return true;
    return false;
}

int main(void) {
    FAST_IO;

    init();
    cin >> n >> m >> S >> V;
    for (int i = 0; i < m; i++) {
        int u, v;
        double rab, cab, rba, cba;
        cin >> u >> v >> rab >> cab >> rba >> cba;
        add(u, v, rab, cab);
        add(v, u, rba, cba);
    }
    if (SPFA(S)) {
        cout << "YES" << endl;
    } else {
        cout << "NO" << endl;
    }
    return 0;
}

POJ - 1511 双向建图

题意

给定一张单向通路图(同时只能单向行驶),然后个人从号点出发,到剩余个宣传点,然后再回到号点汇报结果,求所有人往返路径和的最小值,保证有解。

分析

本题的唯一难点在于的最短路可能不同。所以,需要一次正向存边,一次反向存边,相当于从源点到其他店和从其他到源点,跑两次最短路。

值得一题的是,本想想偷懒,用来存图,但到自闭。。必须使用前向星。愿意应该是本题的数据都是超大,vector的清空,复杂度需要,而前向星清空的复杂度只需要

AC代码

//#include <bits/stdc++.h>
#include <iostream>
#include <iomanip>
#include <bitset>
#include <string>
#include <set>
#include <stack>
#include <sstream>
#include <list>
//#include <array>
#include <vector>
#include <queue>
#include <map>
#include <memory>
#include <iterator>
#include <climits>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define all(x) (x).begin(), (x).end()
typedef long long ll;
typedef unsigned int UINT;
typedef unsigned long long ull;
typedef pair<int, int> pdi;
typedef pair<ll, int> pli;

int const maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
inline int lc(int x)  {return x << 1;}
inline int rc(int x)  {return x << 1 | 1;}
ll dis[2][maxn];

struct node {
    int v, w, next;
}e[2][maxn];
int cnt1, cnt2, head[2][maxn];
int vis[maxn];

void init(int n) {
    memset(head, -1, sizeof(head));
    memset(dis, 0x3f, sizeof(dis));
    cnt1 = 0;
    cnt2 = 0;
}

void add(int id, int u, int v, int w, int &cnt) {
    e[id][cnt].v = v;
    e[id][cnt].w = w;
    e[id][cnt].next = head[id][u];
    head[id][u] = cnt++;
}

void dijkstra(int id) {
    memset(vis, 0, sizeof(vis));
    priority_queue<pair<ll, int> >q;
    q.push(make_pair(0, 1));
    dis[id][1] = 0;
    while (!q.empty()) {
        int u = q.top().second;
        q.pop();
        if (vis[u]) continue;
        vis[u] = 1;
        for (int i = head[id][u]; ~i; i = e[id][i].next) {
            int v = e[id][i].v;
            ll w = e[id][i].w;
            if (dis[id][v] > dis[id][u] + w) {
                dis[id][v] = dis[id][u] + w;
                q.push(make_pair(-dis[id][v], v));
            }
        }
    }
}

int main(void) {
    FAST_IO;

    int t;
    // cin >> t;
    scanf("%d", &t);
    while (t--) {
        int n, m;
        // cin >> n >> m;
        scanf("%d %d", &n, &m);
        init(n);
        for (int i = 0; i < m; i++) {
            int u, v, w;
            // cin >> u >> v >> w;
            scanf("%d %d %d", &u, &v, &w);
            add(0, u, v, w, cnt1);
            add(1, v, u, w, cnt2);
        }
        dijkstra(0);
        dijkstra(1);
        ll ans = 0;
        for (int i = 1; i <= n; i++) {
            // cout << dis[0][i] << "  " << dis[1][i] << endl;
            ans += dis[1][i] + dis[0][i];
        }
        // cout << ans << endl;
        printf("%lld\n", ans);
    }

    return 0;
}

POJ - 3087 暴力模拟

题意

现有字符串s1、s2、s12,其中s1、s2的长度为len,s12的长度为2*len。
是否可以通过一些操作使s1和s2转换合并成s12。无论怎么变换都不会得到s12,那么输出 -1。
变换的操作规则如下:

  • 假设
  • 变换后的序列
  • 如果s和s12完全相同,那么输出变换次数。如果不完全相等,s的前半部分作为s1,后半部分作为s2,重复上述过程。

分析

按照题意,暴力模拟字符即可。。这种水题居然会在搜索里。

AC代码

//#include <bits/stdc++.h>
#include <iostream>
#include <iomanip>
#include <bitset>
#include <string>
#include <set>
#include <stack>
#include <sstream>
#include <list>
//#include <array>
#include <vector>
#include <queue>
#include <map>
#include <memory>
#include <iterator>
#include <climits>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define all(x) (x).begin(), (x).end()
typedef long long ll;
typedef unsigned int UINT;
typedef unsigned long long ull;
typedef pair<int, int> pdi;
typedef pair<ll, int> pli;

int const maxn = 100 + 10;
const int INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
inline int lc(int x)  {return x << 1;}
inline int rc(int x)  {return x << 1 | 1;}


int main(void) {
    FAST_IO;

    int t;
    cin >> t;
    int nt = 0;
    while (t--) {
        int n;
        cin >> n;
        string s1, s2, s, ans;
        cin >> s1 >> s2 >> ans;
        s = s1 + s2;
        int cnt = 0;
        while (s != ans) {
            if (cnt != 0 && s == s1 + s2) {
                cnt = -1;
                break;
            }
            // cout << s << endl;
            string temp = s;
            for (int i = 0, j = 0; i < 2 * n; i += 2, j++) {
                s[i] = temp[n + j];
                s[i + 1] = temp[j];
            }
            cnt++;
        }
        cout << ++nt << " " << cnt << endl;
    }

    return 0;
}

HDU - 1556 差分裸题

题意

有n个气球,给出M条染色信息表示这个区间内被染色1次。求最后每个气球的染色次数。

分析

本题N、M、L、R的范围都是。暴力跑一定超时。

这时候可以采用差分数组。

。而原数组可以同差分数组前缀和获得,即查询

而若是需要对区间内的元素同时增加,可以通过差分完成。证明此处省略。更新。

AC代码

//#include <bits/stdc++.h>
#include <iostream>
#include <iomanip>
#include <bitset>
#include <string>
#include <set>
#include <stack>
#include <sstream>
#include <list>
//#include <array>
#include <vector>
#include <queue>
#include <map>
#include <memory>
#include <iterator>
#include <climits>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define all(x) (x).begin(), (x).end()
typedef long long ll;
typedef unsigned int UINT;
typedef unsigned long long ull;
typedef pair<int, int> pdi;
typedef pair<ll, int> pli;

int const maxn = 100000 + 10;
const int INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
inline int lc(int x)  {return x << 1;}
inline int rc(int x)  {return x << 1 | 1;}

int d[maxn];

int main(void) {
    FAST_IO;

    int n;
    while (cin >> n) {
        if (n == 0) break;
        // for (int i = 1; i <= n; i++) d[i] = 1;
        memset(d, 0, sizeof(d));
        for (int i = 1; i <= n; i++) {
            int l, r;
            cin >> l >> r;
            d[l]++;
            d[r + 1]--;
        }
        for (int i = 1; i <= n; i++) {
            d[i] = d[i] + d[i - 1];
            cout << d[i];
            if (i != n) cout << " ";
        }
        cout << endl;
    }    

    return 0;
}

送外卖 美团面试题 反向建图,两次搜索

题意

外卖员从0开始到n-1送外卖,都到一个点,就有两种选择,用字母ab来表示选择的方式。要求输出字典序最小的情况,无法到达输出,如果字符串无限长,就输出

分析

首先需要判定是否可以到达点,本题是一个有向图,所有需要判断终点和起点是否连通,并且确定哪些点是可达点。

  • 所以先反向建图,从终点开始经行一次,记录可达点,并且确定连通性。若是不连通则输出"No solution!"。

  • 再从0点开始,更加可达点二次搜索,因为要字典序最小,所以若是a可达就优先选择a的方案。若是其中存在回路,则表示字符串无限长。

AC代码

//#include <bits/stdc++.h>
#include <bitset>
#include <iomanip>
#include <iostream>
#include <list>
#include <set>
#include <sstream>
#include <stack>
#include <string>
//#include <array>
#include <algorithm>
#include <cassert>
#include <climits>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iterator>
#include <map>
#include <memory>
#include <queue>
#include <vector>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define all(x) (x).begin(), (x).end()
typedef long long ll;
typedef unsigned int UINT;
typedef unsigned long long ull;
typedef pair<int, int> pdi;
typedef pair<ll, int> pli;

int const maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
inline int lc(int x) { return x << 1; }
inline int rc(int x) { return x << 1 | 1; }

struct node {
    int v, next;
} e[maxn];
int head[maxn], cnt, flag;
int a[maxn], b[maxn];
int vis[maxn];
string ans;
int n;

void add(int u, int v) {
    e[cnt].v = v;
    e[cnt].next = head[u];
    head[u] = cnt++;
}

void init(void) {
    memset(head, -1, sizeof(head));
    cnt = 0;
}

void dfs(int u) {
    if (vis[u]) return;
    vis[u] = 1;
    for (int i = head[u]; ~i; i = e[i].next) {
        dfs(e[i].v);
    }
}

void dfs2(int u, int step) {
    if (u >= n || u < 0 || !vis[u] || step >= n) {
        return;
    }
    if (u == n - 1) {
        flag = 1;
        return;
    }
    int v1 = u + a[u], v2 = u + b[u];
    if (vis[v1] && v1 < n && v1 >= 0) {
        ans += 'a';
        dfs2(v1, step + 1);
    } else if (vis[v2] && v2 < n && v2 >= 0) {
        ans += 'b';
        dfs2(v2, step + 1);
    }
}

int main(void) {
    FAST_IO;
    init();
    cin >> n;

    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    for (int i = 0; i < n; i++) {
        cin >> b[i];
    }
    for (int i = 0; i < n; i++) {
        if (i + a[i] >= 0 && i + a[i] < n) {
            add(i + a[i], i);
        }
    }
    for (int i = 0; i < n; i++) {
        if (i + b[i] >= 0 && i + b[i] < n) {
            add(i + b[i], i);
        }
    }
    dfs(n - 1);
    if (!vis[0]) {
        cout << "No solution!" << endl;
    } else {
        ans = "";
        flag = 0;
        dfs2(0, 0);
        if (!flag)
            cout << "Infinity!" << endl;
        else
            cout << ans << endl;
    }

    return 0;
}

题意

有n块石头,每块石头有两种属性,同时对于每块石头有两种不同的操作:

  • x+a[i], y-b[i]

  • y+c[i], x-d[i]

数值不会变为负数,即任何时候,如果数值小于了0,它会立即变为0

求若按照1-n的顺序操作视同,如何是的最后x*y的值最大。

对于20%的数据,1≤n≤2
对于100%的数据,1≤n≤15,0≤ai,bi,ci,di≤1,000,000

分析

数据较小,可以使用搜索或则状态压缩枚举所有过程。

AC代码

状压解法

//#include <bits/stdc++.h>
#include <iostream>
#include <iomanip>
#include <bitset>
#include <string>
#include <set>
#include <stack>
#include <sstream>
#include <list>
#include <array>
#include <vector>
#include <queue>
#include <map>
#include <memory>
#include <iterator>
#include <climits>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define all(x) (x).begin(), (x).end()
typedef long long ll;
typedef unsigned int UINT;
typedef unsigned long long ull;
typedef pair<int, int> pdi;
typedef pair<ll, int> pli;

int const maxn = 15 + 10;
inline int lc(int x)  {return x << 1;}
inline int rc(int x)  {return x << 1 | 1;}

ll a[maxn], b[maxn], c[maxn], d[maxn];
ll dp[maxn];

int main(void) {
    FAST_IO;

    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> a[i] >> b[i] >> c[i] >> d[i];
    }

    ll ans = 0;
    for (int i = 0; i < (1 << n); i++) {
        ll x = 0, y = 0;
        for (int j = 0; j < n; j++) {
            if (i & (1 << j)) {
                x += a[j];
                y -= b[j];
            } else {
                x -= d[j];
                y += c[j];
            }
            x = x < 0 ? 0 : x;
            y = y < 0 ? 0 : y;
        }
        ans = max(ans, x * y);
    }
    cout << ans << endl;

    return 0;
}

初心如金 结论,瞎搞

题意

详细见链接。

分析

可以更加当前输入数字的奇偶性判断,因为输入的都是奇数,所以若是出现偶数,则表示二进制末尾的几个是,否则便是,因此可以直接根据后面询问的奇偶性判断上一次的答案。瞎搞的结论题。

AC代码

//#include <bits/stdc++.h>
#include <iostream>
#include <iomanip>
#include <bitset>
#include <string>
#include <set>
#include <stack>
#include <sstream>
#include <list>
#include <array>
#include <vector>
#include <queue>
#include <map>
#include <memory>
#include <iterator>
#include <climits>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define all(x) (x).begin(), (x).end()
typedef long long ll;
typedef unsigned int UINT;
typedef unsigned long long ull;
typedef pair<int, int> pdi;
typedef pair<ll, int> pli;

int const maxn = 1e5 + 10;
inline int lc(int x)  {return x << 1;}
inline int rc(int x)  {return x << 1 | 1;}

template <typename T>
int read(T &x) {
    x = 0;
    int ch = getchar(), f = 1;
    while (ch > '9' || ch < '0') {
        if (ch == EOF) return 0;
        if (ch == '-') f = -f;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    x *= f;
    return 1;
}
template <typename T>
void out(T x) {
    if (x < 0) putchar('-'),x = -x;
    if (x > 9) out(x / 10);
    putchar(x % 10 + '0');
}

int isprime(ll a) {
    if (a == 1) return 0;
    for (ll i = 2; i * i <= a; ++i) {
        if (a % i == 0) return 0;
    }
    return 1;
}

int main(void) {
    FAST_IO;

    int n;
    read(n);
    ll a;
    int x = 0;
    read(a);
    // x = isprime(a);
    for (int i = 1; i < n; i++) {
        read(a);
        out(!(a & 1));
        printf("\n");
    }

    return 0;
}

最短Hamilton路径 状压 Floyd最短路

题意

给定一张 n 个点的带权无向图,点从标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。

对于任意的x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且a[x,y]+a[y,z]≥a[x,z]a[x,y]+a[y,z] \geq a[x,z]a[x,y]+a[y,z]≥a[x,z]。

分析

首先我们要思考如果让这个NP完全题目复杂度降低,那么可以优先考虑到使用位运算,状态压缩等解决思路。
然后接着思考,我们可以发现,我们所需要的不是整个方案,而只是方案最优解,所以我们只需要记录当前这个方案的最优解即可,那么我们考虑的状态,不久只有,在当前方案i中,目前抵达的点是j。
现在既然装填已经确定好了当前点j,那么这个j点是由哪一个状态移动而来的呢?我们可以选择k,也就是说我们的状态转移方程可以为

i^(1<<j)的意思是,i 异或 1右移j位,具体来说就是i这个方案集合 xor 10……0,(其中1的位置在第j位)。其作用是:

  • 第一点它是在判断第j位的情况
  • 第二点位运算速度快

AC代码

#include <bits/stdc++.h>
#define FAST_IO std::ios::sync_with_stdio(false),std::cin.tie(nullptr),std::cout.tie(nullptr)
using namespace std;
typedef long long ll;
typedef unsigned int UINT;
typedef unsigned long long ull;
int const maxn = 21;
int dp[1 << maxn][maxn];
int d[maxn][maxn];
int const INF = 0x3f3f3f3f;

int main(void) {
    FAST_IO;
    memset(dp, INF, sizeof(dp));
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> d[i][j];
        }
    }
    dp[1][0] = 0;
    for (int i = 1; i < (1 << n); i++) {
        for (int j = 0; j < n; j++) {
            if (i >> j & 1) {
                for (int k = 0; k < n; k++) {
                    if ((i ^ 1 << j) >> k & 1) {
                        dp[i][j] = min(dp[i][j], dp[i ^ 1 << j][k] + d[k][j]);
                    }
                }
            }
        }
    }
    cout << dp[(1 << n) - 1][n - 1] << endl;

    return 0;
}