hdu3315-My Brute(费用流 or KM算法)

题目:My Brute

Seaco是一个漂亮的妹子,喜欢玩一款名叫My Brute的游戏。情人节快到了,starvae和xingxing都想邀请妹子过节,但是妹子只能陪一个啊,于是两个人决定打一架,用男人的方式对决,来一场My Brute吧!

一开始两个人都有n(n<100)只宠物,每个宠物有生命值,伤害值,每次两个人各派出一只宠物,starvae可以任意确定宠物的出场顺序,xingxing不可以。

每局开始starvae先打,hp<=0的一方输。

starvvae的第i个宠物(因为可以换顺序,也就是可能不是第i个出场的)赢vi分,否则减vi分。如果结束时starvae的分数大于0,starvae就赢了。

现在请你求出如果能赢,输出最大分数,和出场顺序和原顺序的相似度(如果最大分数有多种可能的安排方法,选择相似度最大的方法)。不能赢就输出Oh, I lose my dear seaco!

题解:@g3&wy%*¥…sd…#%…

我没做出来。。。因为窝读错题了。。。哭T^T。。。查题解不小心就查到了答案。。。我的英语绝对没救了。。。虽然不知道读对也不知道能不能做出来。。。。。

很好想的是源点连每一个starvae的宠物,流量是1,权值为0,xingxing的每个宠物的连汇点,流量是1,权值为0,两个人的宠物两两相连,能赢权值是v[i],否则是-v[i]。

但是这里求得是最大值,以前也做过求最大值,方法是加边加-w,但这里不行,因为边本来就是有正有负。

所以每次spfa求最短路改为求最长路。记得初始化时是-inf。

还有一个问题是。。。如何保证尽可能原顺序。。。于是有一个比较巧妙的方法,就是把所有v[i]*100,然后把每一个顺序不变的边,权值+1,因为点的个数是100以内的,所以能保证结果正确。。

#include <cstring>
#include <vector>
#include <queue>
#include <cstdio>
using namespace std;
typedef long long ll;

const int MAXV = 410;
const int INF = 1<<30;

struct Edge { int to, cap, cost, rev; };
vector<Edge> G[MAXV];
int dist[MAXV], prv[MAXV], pre[MAXV], in[MAXV];
queue<int> que;

void addedge(int from, int to, int cap, int cost) {
    G[from].push_back((Edge){to, cap, cost, G[to].size()});
    G[to].push_back((Edge){from, 0, -cost, G[from].size()-1});
}

int min_cost_max_flow(int s, int t) { //, int f) {
    int res = 0;
    int f = 0;
    while (1) { //f > 0) {
        for (int i = 0; i <= t; ++i) dist[i] = -INF, in[i] = 0;
        dist[s] = 0;
        while (!que.empty()) que.pop();
        in[s] = 1;
        que.push(s);

        while (!que.empty()) {
            int u = que.front(); que.pop(); in[u] = 0;
            for (int i = 0; i < G[u].size(); ++i) {
                Edge &e = G[u][i];
                if (e.cap > 0 && dist[e.to] < dist[u] + e.cost) {
                    dist[e.to] = dist[u] + e.cost;
                    prv[e.to] = u;
                    pre[e.to] = i;
                    if (in[e.to] == 0) {
                        in[e.to] = 1;
                        que.push(e.to);
                    }
                }
            }
        }

        if (dist[t] == -INF) break; //return -1;

        int d = INF; // d = f;
        for (int v = t; v != s; v = prv[v]) {
            d = min(d, G[prv[v]][pre[v]].cap);
        }
        f += d;
        res += d * dist[t];
        for (int v = t; v != s; v = prv[v]) {
            Edge &e = G[prv[v]][pre[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return res;
}

const int N = 100;
int v[N], h[N], p[N], a[N], b[N];

bool win(int h, int p, int a, int b) { // hp damage
    int x = h/b; if (h % b != 0) x++;
    int y = p/a; if (p % a != 0) y++;
    return x >= y;
}

int main()
{
    int n;
    while (~scanf("%d", &n) && n) {
        for (int i = 0; i < n; ++i) scanf("%d", &v[i]);
        for (int i = 0; i < n; ++i) scanf("%d", &h[i]);
        for (int i = 0; i < n; ++i) scanf("%d", &p[i]);
        for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
        for (int i = 0; i < n; ++i) scanf("%d", &b[i]);

        int src = 0, sink = n+n+1;

        for (int i = src; i <= sink; ++i) G[i].clear();

        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                int tmp = win(h[i], p[j], a[i], b[j]) ? v[i] : -v[i];
                tmp *= 100; if (i == j) tmp++;
                addedge(i+1, n+j+1, 1, tmp);
            }
        }

        for (int i = 0; i < n; ++i) {
            addedge(src, i+1, 1, 0);
            addedge(i+1+n, sink, 1, 0);
        }

        int ans = min_cost_max_flow(src, sink);

        if (ans/100 <= 0) printf("Oh, I lose my dear seaco!\n");
        else printf("%d %.3f%%\n", ans/100, ans%100/(double)n*100);

    }
    return 0;
}

既然很明显是二分图

所以可以考虑一下KM解法,建图什么的都一样辣,速度还是一如既往的完爆费用流。。

#include <cstring>
#include <vector>
#include <queue>
#include <cstdio>
using namespace std;
typedef long long ll;
const int MAXN = 410;
const int INF = 0x3f3f3f3f;

int G[MAXN][MAXN];
int vx[MAXN], vy[MAXN];
bool visx[MAXN], visy[MAXN];
int match[MAXN];
int slack[MAXN];

int N;

bool dfs(int x)
{
    visx[x] = true;

    for (int y = 0; y < N; ++y) {

        if (visy[y]) continue;

        int gap = vx[x] + vy[y] - G[x][y];

        if (gap == 0) {
            visy[y] = true;
            if (match[y] == -1 || dfs( match[y] )) {
                match[y] = x;
                return true;
            }
        } else {
            slack[y] = min(slack[y], gap);
        }
    }

    return false;
}

int KM()
{
    memset(match, -1, sizeof match);
    memset(vy, 0, sizeof vy);

    for (int i = 0; i < N; ++i) {
        vx[i] = G[i][0];
        for (int j = 1; j < N; ++j) {
            vx[i] = max(vx[i], G[i][j]);
        }
    }

    for (int i = 0; i < N; ++i) {

        fill(slack, slack + N, INF);

        while (1) {
            memset(visx, false, sizeof visx);
            memset(visy, false, sizeof visy);

            if (dfs(i)) break;

            int d = INF;
            for (int j = 0; j < N; ++j)
                if (!visy[j]) d = min(d, slack[j]);

            for (int j = 0; j < N; ++j) {
                if (visx[j]) vx[j] -= d;
                if (visy[j]) vy[j] += d;
                else slack[j] -= d;
            }
        }
    }

    int res = 0;
    for (int i = 0; i < N; ++i)
        res += G[ match[i] ][i];

    return res;
}

int v[MAXN], h[MAXN], p[MAXN], a[MAXN], b[MAXN];

bool win(int h, int p, int a, int b) { // hp damage
    int x = h/b; if (h % b != 0) x++;
    int y = p/a; if (p % a != 0) y++;
    return x >= y;
}

int main()
{
    int n;
    while (~scanf("%d", &n) && n) { N = n;
        for (int i = 0; i < n; ++i) scanf("%d", &v[i]);
        for (int i = 0; i < n; ++i) scanf("%d", &h[i]);
        for (int i = 0; i < n; ++i) scanf("%d", &p[i]);
        for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
        for (int i = 0; i < n; ++i) scanf("%d", &b[i]);

        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                int tmp = win(h[i], p[j], a[i], b[j]) ? v[i] : -v[i];
                tmp *= 100; if (i == j) tmp++;
                G[i][j] = tmp;
            }
        }

        int ans = KM();

        if (ans/100 <= 0) printf("Oh, I lose my dear seaco!\n");
        else printf("%d %.3f%%\n", ans/100, ans%100/(double)n*100);

    }
    return 0;
}

时间: 2024-10-09 17:07:15

hdu3315-My Brute(费用流 or KM算法)的相关文章

hdu 3315 My Brute 费用流,费用最小且代价最小

很常见的想法了= = #include <stdio.h> #include <iostream> #include <string.h> using namespace std; const int N=400; const int MAXE=200000; const int inf=1<<30; int head[N],s,t,cnt,n,m,ans; int d[N],pre[N]; bool vis[N]; int q[MAXE]; int V[N

HDU 3315 My Brute(费用流)

题目地址:HDU 3315 这个题的思路完全是自己想出来的,自我感觉挺巧妙的...(大牛勿喷...)对大胆建图又多了一份信心. 具体思路是构造一个二分图,Si连源点,Xi连汇点,流量都是1,费用0.然后当Si可以赢Xj的时候,就对这两人连一条边,费用值为-Vi*1000,如果i==j的话,费用值就再减1,因为题目要求尽量不改变原先的顺序,所以说应该尽量让序号相同的对打.而费用值减1的话,会优先考虑序号相同的,而且让费用扩大了1000倍,此时也不会改变主要的分数因素大小.同理,输的话,费用值为Vi

HDU 4862 Jump 费用流

又是一个看了题解以后还坑了一天的题…… 结果最后发现是抄代码的时候少写了一个负号. 题意: 有一个n*m的网格,其中每个格子上都有0~9的数字.现在你可以玩K次游戏. 一次游戏是这样定义的: 你可以选任意之前没有走过的格子作为起点.然后走任意步,其中每一步你可以向右或者向下走任意格.假如从(x1, y1)走到(x2, y2)需要花费能量|x1-x2|+|y1-y2|-1,如果这一步和上一步格子的数字相同,那么可以获得格子上相应数字的能量.能量可以为负值. 问你,在K次以内走完所以格子最多能得到多

HDU 3488--Tour(KM or 费用流)

因为每个点只能经过一次 所以考虑拆点 这题有坑,有重边.. KM算法 把一个点拆成入点和出点 入点在X部,出点在Y步. 如果u,v之间有路径,就在X部的u点连接Y部的v点 求完美匹配. 当完美匹配的时候,每个点都有一个入度和一个出度,可知成环. 因为完美匹配求得是最大匹配 记得把每条边权值取相反数 #include <iostream> #include <cstring> #include <cstdio> using namespace std; const int

hdoj 3435 A new Graph Game 【无向图判断权值最小哈密顿环】【KM算法】

A new Graph Game Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1934    Accepted Submission(s): 827 Problem Description An undirected graph is a graph in which the nodes are connected by undir

hdoj 3488 Tour 【最小费用最大流】【KM算法】

Tour Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 2299    Accepted Submission(s): 1151 Problem Description In the kingdom of Henryy, there are N (2 <= N <= 200) cities, with M (M <= 3000

hdu 3488(KM算法||最小费用最大流)

Tour Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 2925    Accepted Submission(s): 1407 Problem Description In the kingdom of Henryy, there are N (2 <= N <= 200) cities, with M (M <= 30000

[BZOJ1937][SHOI2004]Mst最小生成树(KM算法,最大费用流)

1937: [Shoi2004]Mst 最小生成树 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 802  Solved: 344[Submit][Status][Discuss] Description Input 第 一行为N.M,其中 表示顶点的数目, 表示边的数目.顶点的编号为1.2.3.…….N-1.N.接下来的M行,每行三个整数Ui,Vi,Wi,表示顶点Ui与Vi之间有一条边,其权值为 Wi.所有的边在输入中会且仅会出现一次.再接着N-1

KM算法 PK 最小费用最大流

用到了KM算法 ,发现自己没有这个模板,搜索学习一下上海大学final大神,http://www.cnblogs.com/kuangbin/p/3228861.html 1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #include <iostream> 5 using namespace std; 6 7 /* KM算法 8 * 复杂度O(nx*nx*ny) 9 *