最新文章

[HDU4903]The only survival 题解

[HDU4903]The only survival 题解

题目地址:HDUOJ:Problem – 4903 题目描述 There is 

[HDU6166]Senior Pan 题解

[HDU6166]Senior Pan 题解

题目地址:HDUOJ:Problem – 6166 题目描述 Senior P 

[HNOI2009]最小圈 题解

[HNOI2009]最小圈 题解

题目地址:洛谷:【P3199】[HNOI2009]最小圈 – 洛谷、BZOJ:Problem 1486. — [HNOI2009]最小圈

题目描述

换句话说就是找图中的环使得环上的边权的平均数最小。

输入输出格式

输入格式:
n, m
u, v, w

输出格式:
答案。

输入输出样例

输入样例#1:

4 5
1 2 5
2 3 5
3 1 5
2 4 3
4 1 3

输出样例#1:

3.66666667

输入样例#2:

2 2
1 2 -2.9
2 1 -3.1

输出样例#2:

-3.00000000

说明

n<=3000, m<=10000

题解

我们先考虑直接找环求平均数是不太可行的,因为图上环可以很多,边数的数据范围不允许平方。但是如果我们有一个数,我们可以验证是否存在满足平均数比这个数大/小的环。
具体而言,我们用看上去很快的被称为DFS-SPFA的东西跑一遍,跑的时候dis数组全部初值为0,边权减掉我们固定的那个数,然后判负环即可。存在负环==存在平均数比这个数小的环。那么外面套个二分答案即可。

代码

// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>

#include <vector>
#include <algorithm>

typedef long long LL;

const int MAXN = 3005;
const double EPS = 1e-10;

struct Edge {
    int to;
    double w;
};

std::vector<Edge> gra[MAXN];
int n, m, ut, vt;
double wt, dis[MAXN];
bool vis[MAXN];

inline bool dfs(int u, double x) {
    vis[u] = true;
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i].to;
        if(dis[v] > dis[u] + gra[u][i].w - x + EPS) {
            if(vis[v]) return true;
            dis[v] = dis[u] + gra[u][i].w - x;
            if(dfs(v, x)) return true;
        }
    }
    vis[u] = false;
    return false;
}

inline bool check(double x) {
    memset(vis, 0, sizeof(vis));
    memset(dis, 0, sizeof(dis));
    for(int i = 1; i <= n; i++) {
        if(vis[i]) continue;
        // dfs returns true represents that there is a negative circle
        if(dfs(i, x)) return true;
    }
    return false;
}

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i++) {
        scanf("%d%d%lf", &ut, &vt, &wt);
        gra[ut].push_back(Edge{vt, wt});
    }
    double l = -1e5, r = 1e5;
    while(r - l > EPS) {
        double mid = (l + r) / 2;
        if(check(mid)) r = mid; else l = mid;
    }
    printf("%.8lf", r);
    return 0;
}
[CF295B]Greg and Graph 题解

[CF295B]Greg and Graph 题解

题目地址:Codeforces:Problem – 295B –  

[WF2012]Infiltration 题解

[WF2012]Infiltration 题解

题目地址:UVa:UVa Online Judge 题目描述 原题面参见UVa/vjudg 

[BZOJ2143]飞飞侠 题解

[BZOJ2143]飞飞侠 题解

题目地址:BZOJ:Problem 2143. — 飞飞侠

题目描述

飞飞国是一个传说中的国度,国家的居民叫做飞飞侠。飞飞国是一个N×M的矩形方阵,每个格子代表一个街区。然而飞飞国是没有交通工具的。飞飞侠完全靠地面的弹射装置来移动。每个街区都装有弹射装置。使用弹射装置是需要支付一定费用的。而且每个弹射装置都有自己的弹射能力。我们设第i行第j列的弹射装置有Aij的费用和Bij的弹射能力。并规定有相邻边的格子间距离是1。那么,任何飞飞侠都只需要在(i,j)支付Aij的费用就可以任意选择弹到距离不超过Bij的位置了。
现在的问题很简单。有三个飞飞侠,分别叫做X,Y,Z。现在它们决定聚在一起玩,于是想往其中一人的位置集合。告诉你3个飞飞侠的坐标,求往哪里集合大家需要花的费用总和最低。

输入输出格式

输入格式:
输入的第一行包含两个整数N和M,分别表示行数和列数。
接下来是2个N×M的自然数矩阵,为Aij和Bij
最后一行六个数,分别代表X,Y,Z所在地的行号和列号。
1<=N,M<=150;0<=Aij<=10^9;0<=Bij<=1000
注:其实AB矩阵的意义是反着的。

输出格式:
第一行输出一个字符X、Y或者Z。表示最优集合地点。
第二行输出一个整数,表示最小费用。如果无法集合,只输出一行NO

输入输出样例

输入样例#1:

4 4
0 0 0 0
1 2 2 0
0 2 2 1
0 0 0 0
5 5 5 5
5 5 5 5
5 5 5 5
5 5 5 5
2 1 3 4 2 2

输出样例#1:

Z
15

题解

我们考虑类似分层图的思想。把一次跳跃看成是经过该点时给自己续了那么多距离步可以继续走,而只有当剩余步数为0的时候才能够续下一次。那么用dis[i][j][k]来表示在(i, j)这个点且剩余步数为k的情况的最少花费,使用Dijkstra来转移这些三维点,问题就变成了3次单源最短路径的处理。Dijkstra可以使用堆优化加速。
答案是什么?如果我们这一次求出了从X所在位置(xx, xy)出发到(yx, yy)(zx, zy)的距离(dis[yx][yy][0]和dis[zx][zy][0]),这两个数是对在Y位置集合和在Z位置集合的答案产生贡献的,那么把它们加到相应的答案里,再判断谁最小就好啦。
复杂度是Dijkstra的复杂度,这里的点的规模是O(n^3)的,边的规模也如此。
吐槽:调了一下午,结果发现不是Dijkstra的问题而是答案求最小值的问题,好气啊qwq

代码

// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>

#include <algorithm>
#include <queue>
#include <vector>
#include <functional>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 155;
const int INF = 1e9;
const int fix[2][5] = {{0, -1, 1, 0, 0}, {0, 0, 0, -1, 1}};

int n, m, mx, cost[MAXN][MAXN], dis[MAXN][MAXN], x1, y1, x2, y2, x3, y3, d[MAXN][MAXN][MAXN << 1], a1, a2, b1, b2, c1, c2, ans = INF;
char ansc;
bool vis[MAXN][MAXN][MAXN << 1];

struct Point {
    int x, y, k;
    int w;
    Point() {}
    Point(int x, int y, int k, LL w): x(x), y(y), k(k), w(w) {}
};

inline bool operator >(Point a, Point b) {
    return a.w > b.w;
}

std::priority_queue<Point, std::vector<Point>, std::greater<Point> > heap;

inline void calsp(int sx, int sy) {
    while(!heap.empty()) heap.pop();
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            for(int k = 0; k <= mx; k++) {
                vis[i][j][k] = 0;
                d[i][j][k] = INF;
            }
        }
    }
    vis[sx][sy][0] = 1;
    d[sx][sy][dis[sx][sy]] = cost[sx][sy];
    heap.push(Point(sx, sy, dis[sx][sy], cost[sx][sy]));
    while(!heap.empty() && (!vis[x1][y1][0] || !vis[x2][y2][0] || !vis[x3][y3][0])) {
        Point u = heap.top();
        heap.pop();
        if(vis[u.x][u.y][u.k]) continue;
        vis[u.x][u.y][u.k] = 1;
        if(u.k) {
            for(int i = 0; i < 5; i++) {
                int nx = u.x + fix[0][i], ny = u.y + fix[1][i];
                if(nx < 1 || nx > n || ny < 1 || ny > m || vis[nx][ny][u.k - 1]) continue;
                if(d[u.x][u.y][u.k] < d[nx][ny][u.k - 1]) {
                    d[nx][ny][u.k - 1] = d[u.x][u.y][u.k];
                    heap.push(Point(nx, ny, u.k - 1, d[nx][ny][u.k - 1]));
                }
            }
        } else {
            if(d[u.x][u.y][0] + cost[u.x][u.y] < d[u.x][u.y][dis[u.x][u.y]]) {
                d[u.x][u.y][dis[u.x][u.y]] = d[u.x][u.y][0] + cost[u.x][u.y];
                heap.push(Point(u.x, u.y, dis[u.x][u.y], d[u.x][u.y][dis[u.x][u.y]]));
            }
        }
    }
}

int main() {
    n = readint(); m = readint(); mx = n + m - 2;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            dis[i][j] = readint();
            dis[i][j] = std::min(dis[i][j], std::max(mx - i - j, i + j - 2));
        }
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            cost[i][j] = readint();
        }
    }
    x1 = readint(); y1 = readint();
    x2 = readint(); y2 = readint();
    x3 = readint(); y3 = readint();
    calsp(x1, y1); a1 = d[x2][y2][0]; a2 = d[x3][y3][0];
    calsp(x2, y2); b1 = d[x1][y1][0]; b2 = d[x3][y3][0];
    calsp(x3, y3); c1 = d[x1][y1][0]; c2 = d[x2][y2][0];
    if(b1 + c1 < ans) ans = b1 + c1, ansc = 'X';
    if(a1 + c2 < ans) ans = a1 + c2, ansc = 'Y';
    if(a2 + b2 < ans) ans = a2 + b2, ansc = 'Z';
    if(ans == INF) puts("NO"); else printf("%c\n%d\n", ansc, ans);
    return 0;
}
[CF543B]Destroying Roads 题解

[CF543B]Destroying Roads 题解

题目地址:Codeforces:Problem – 666B –  

[CF666B]World Tour 题解

[CF666B]World Tour 题解

题目地址:Codeforces:Problem – 666B –  

[BZOJ3900]交换茸角 题解

[BZOJ3900]交换茸角 题解

题目地址:BZOJ:Problem 3900. — 交换茸角

题目描述

动物园里有 n 头麋鹿。每头麋鹿有两支茸角,每支茸角有一个重量。然而,一旦某头麋鹿上两支茸角的重量之差过大,这头麋鹿就会失去平衡摔倒。为了不然这种悲剧发生,动物园院长决定交换某些茸角,使得任意一头麋鹿的两角重量差不超过 c。然而,交换两支茸角十分麻烦,不仅因为茸角需要多个人来搬运,而且会给麋鹿造成痛苦。因此,你需要计算出最少交换次数,使得任意一头麋鹿的两角重量差不超过 c。
注意,交换两支茸角只能在两头麋鹿之间进行。因为交换同一头麋鹿的两支角是没有意义的。

输入输出格式

输入格式:
第一行为整数 n,c。接下来 n 行,每行两个整数,分别表示一开始每头麋鹿的两角重量。

输出格式:
一个数,即最少交换次数。如果无论如何也不能使每头麋鹿平衡,输出 -1。

输入输出样例

输入样例#1:

3 0
3 3
2 5
2 5

输出样例#1:

1

说明

对于 100% 的数据,n <= 16, c <= 1000000, 每支茸角重量不超过 1000000。

题解

这个n的范围看起来很像状压DP,那我们就往那个方向去想。
我们考虑用状压表示子集,dp[S]表示S集合中的所有鹿换角最少多少次能满足条件。对于初始值,我们把S集合中的鹿的所有角都拿出来排个序分好组,如果发现有一组角的差值大过c,那么这个集合内部就无法自行调整为满足条件的情况,dp值应设置为INF。如果集合内部已经满足条件,dp值应设置为0。如果都不是上面的情况,那么按照最差的换法,应该是每个鹿从待选角中找到配对的角换上,这个次数最差是集合内鹿的数量-1的,dp值应设置为这个最差的值。
对于转移,我们考虑枚举每个状态S的子集T,有下面的方程
dp[S] = \min(dp[S], dp[T] + dp[S \wedge T])
这个的复杂度是O(2^n \cdot n)的。

代码

// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>

#include <vector>
#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 17, INF = 0x3f3f3f3f;

int n, c, a[MAXN], b[MAXN], dp[1 << MAXN];
std::vector<int> tmp;

int main() {
    n = readint();
    c = readint();
    for(int i = 1; i <= n; i++) {
        a[i] = readint();
        b[i] = readint();
    }
    memset(dp, 0x3f, sizeof(dp));
    dp[0] = 0;
    for(int i = 1; i < (1 << n); i++) {
        bool flag = false;
        tmp.clear();
        for(int j = 1; j <= n; j++) {
            if(i & (1 << (j - 1))) {
                if(std::abs(a[j] - b[j]) > c) flag = true;
                tmp.push_back(a[j]);
                tmp.push_back(b[j]);
            }
        }
        if(!flag) {
            dp[i] = 0;
            continue;
        }
        std::sort(tmp.begin(), tmp.end());
        flag = false;
        for(int j = 0; j < tmp.size(); j += 2) {
            if(tmp[j + 1] - tmp[j] > c) {
                flag = true;
                break;
            }
        }
        if(!flag) dp[i] = tmp.size() / 2 - 1;
    }
    for(int i = 1; i < (1 << n); i++) {
        for(int j = (i - 1) & i; j; j = (j - 1) & i) {
            dp[i] = std::min(dp[i], dp[j] + dp[i ^ j]);
        }
    }
    if(dp[(1 << n) - 1] == INF) {
        printf("-1");
    } else {
        printf("%d", dp[(1 << n) - 1]);
    }
    return 0;
}
[51Nod1684]子集价值 题解

[51Nod1684]子集价值 题解

题目地址:51Nod:子集价值 问题 – 51Nod 题目描述 lyk最近在研