2012 Multi-University Training Contest 2

官方题解:http://blog.renren.com/blog/601081183/862977450

1001 Hero HDU4310 每个英雄都有HP和DPS,玩家有无穷HP,DPS为1。每回合玩家选择一个英雄攻击,该英雄HP减一,同时玩家HP减去当前活着的英雄的DPS和。问如何攻击对手,损失HP最少。 官方题解上写的是状态压缩DP,其实当时看那么多人都过了,果断贪心,按DPS/HP递减排序攻击就行,我感觉这个贪心是对的吧。。

用dp[mask]表示杀死mask集合的敌人时,这些敌人造成的最小hp消耗。有转移方程dp[mask] = min{dp[mask - {i}] + hp_sum[mask] * dps[i], for all i in mask}

额,相当于先攻击后那个{i}。


#include 
#include 
#include 
using namespace std;
int dp[1 << 20];
int getdps(int x, int *dps)
{
    int ans = 0;
    int i = 1;
    int p = 0;
    while (i <= x)
    {
        if (x & i)
        {
            ans += dps[p];
        }
        i = i << 1;
        ++p;
    }
    return ans;
}
int main()
{
    int n;
    int dps[25], hp[25];
    while (cin >> n)
    {
        memset(dp, 127, sizeof(dp));
        dp[0] = 0;
        for (int i = 0; i < n; i++) cin >> dps[i] >> hp[i];
        for (int i = 1; i < (1 << n); i++)
        {
            int j = 1;
            int p = 0;
            int dpssum = getdps(i, dps);
            while (j <= i)
            {
                int temp = j ^ i;
                dp[i] = min(dp[i], dp[temp] + dpssum * hp[p]);
                j = j << 1;
                ++p;
            }
        }
        cout << dp[(1 << n) - 1] << endl;
    }
    return 0;
}

1002 Meeting point-1 HDU4311 用坐标表示每个人的住处,只能上下左右走。选择一个住处聚会,使所有人到达这里的距离和最小。 刚开始也是没思路,后来xw说,可以分别算x和y方向的距离,先按x排序,算出其他点到第一个点x方向的距离和(sumx[0]),然后递推算出每一个sumx,再按y排序,算出每个sumy。index记录到底是哪个点。


#include 
#include 
#include 
#include 
const int MAXN = 100010;
using namespace std;
struct point
{
    int x, y, index;
}px[MAXN], py[MAXN];
long long sumx[MAXN], sumy[MAXN];
long long ans[MAXN];
bool compx(const point &a;, const point &b;)
{
    return a.x < b.x;
}
bool compy(const point &a;, const point &b;)
{
    return a.y < b.y;
}
int main()
{
    int t, n;
    cin >> t;
    while(t--)
    {
        cin >> n;
        memset(sumx, 0, sizeof(sumx));
        memset(sumy, 0, sizeof(sumy));
        memset(ans, 0, sizeof(ans));
        for (int i = 0; i < n; i++)
        {
            scanf("%d%d", &px;[i].x, &px;[i].y);
            px[i].index = i;
            py[i] = px[i];
        }
        sort(px, px+n, compx);
        sort(py, py+n, compy);

        for (int i = 1; i < n; i++)
        {
            sumx[0] += px[i].x - px[0].x;
            sumy[0] += py[i].y - py[0].y;
        }
        ans[px[0].index] += sumx[0];
        ans[py[0].index] += sumy[0];
        for (int i = 1; i < n; i++)
        {
            sumx[i] = sumx[i-1] + (px[i].x - px[i-1].x) * (long long)(2*i - n);
            sumy[i] = sumy[i-1] + (py[i].y - py[i-1].y) * (long long)(2*i - n);
            //sumx[i] = sumx[i-1] - (px[i].x - px[i-1].x) * (n - i) + ;
            //sumy[i] = sumy[i-1] - (py[i].y - py[i-1].y) * (n - i);
            ans[px[i].index] += sumx[i];
            ans[py[i].index] += sumy[i];
        }
        /*
        cout <<"sumx" << endl;
        for (int i = 0; i < n; i++) cout << sumx[i] << endl;
        cout << "sumy" << endl;
        for (int i = 0; i < n; i++) cout << sumy[i] << endl;
        */

        long long bestans = 999999999999999LL;
        for (int i = 0; i < n; i++)
        {
            if (ans[i] < bestans) bestans = ans[i];
        }
        cout << bestans << endl;
    }
    return 0;
}

Meeting point-2 HDU4312 和上一道题的意思基本一样,这次可以斜着走,斜着走距离仍为1而不是根号二。 看了题解才知道,原来还有切比雪夫距离曼哈顿距离

对于原坐标系中两点间的 Chebyshev 距离,是将坐标轴顺时针旋转45度并将所有点的坐标值放大sqrt(2)倍所得到的新坐标系中的Manhattan距离的二分之一。

这个挺神奇的,现在曼哈顿距离为2的两个点在原坐标系里切比雪夫距离为1。新坐标为 (x-y, x+y),怎么算的呢?转换成极坐标然后就能算了,刚才我都忘了= =然后还用上道题的代码就可以了。


#include 
#include 
#include 
#include 
const int MAXN = 100010;
using namespace std;
struct point
{
    long long x, y;
    int index;
}px[MAXN], py[MAXN];
long long sumx[MAXN], sumy[MAXN];
long long ans[MAXN];
bool compx(const point &a;, const point &b;)
{
    return a.x < b.x;
}
bool compy(const point &a;, const point &b;)
{
    return a.y < b.y;
}
int main()
{
    int t, n;
    cin >> t;
    while(t--)
    {
        cin >> n;
        memset(sumx, 0, sizeof(sumx));
        memset(sumy, 0, sizeof(sumy));
        memset(ans, 0, sizeof(ans));
        long long tempx, tempy;
        for (int i = 0; i < n; i++)
        {
            //cin >> tempx >> tempy;
            scanf("%I64d%I64d", &tempx;, &tempy;);
            px[i].index = i;
            px[i].x = tempx - tempy;
            px[i].y = tempx + tempy;
            py[i] = px[i];
        }
        sort(px, px+n, compx);
        sort(py, py+n, compy);

        for (int i = 1; i < n; i++)
        {
            sumx[0] += px[i].x - px[0].x;
            sumy[0] += py[i].y - py[0].y;
        }
        ans[px[0].index] += sumx[0];
        ans[py[0].index] += sumy[0];
        for (int i = 1; i < n; i++)
        {
            sumx[i] = sumx[i-1] + (px[i].x - px[i-1].x) * (long long)(2*i - n);
            sumy[i] = sumy[i-1] + (py[i].y - py[i-1].y) * (long long)(2*i - n);
            ans[px[i].index] += sumx[i];
            ans[py[i].index] += sumy[i];
        }
        long long bestans = 999999999999999LL;
        for (int i = 0; i < n; i++)
        {
            if (ans[i] < bestans) bestans = ans[i];
        }
        cout << bestans / 2 << endl;
    }
    return 0;
}

1004 Matrix HDU4313 一棵树上有K个机器,要使这K个机器互不相连,使去掉的边的权值和最小。 类似于Kruskal求最小生成树,边由大到小排序,如果某条边使两个机器连通就放弃这条边。因为其它的边一定比这条边长。


#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 100010;
struct e
{
    int u, v, w;
}edge[MAXN];

int p[MAXN], rank[MAXN];
bool machine[MAXN];
int n;

bool comp(const e &a;, const e &b;)
{
    return a.w > b.w;
}

void make_set()
{
    for (int i = 0; i < n; i++) // ?
    {
        p[i] = i;
        rank[i] = 0;
    }
}

int find_set(int x)
{
    if (x != p[x]) p[x] = find_set(p[x]);
    return p[x];
}

long long kruskal(int edgecount)
{
    int x, y;
    long long ans = 0;
    make_set();
    sort(edge, edge + edgecount, comp);
    for (int i = 0; i < edgecount ; i++)
    {
        x = find_set(edge[i].u);
        y = find_set(edge[i].v);
        if (machine[x] && machine[y]) continue;
        if (machine[x] || machine[y])
            machine[x] = machine[y] = true;
        if (x != y)
        {
            ans += edge[i].w;
            if (rank[x] > rank[y]) p[y] = x;
            else
            {
                p[x] = y;
                if (rank[x] == rank[y]) rank[y]++;
            }
        }
    }
    return ans;
}
int main()
{
    int t, k;
    cin >> t;
    while (t--)
    {
        cin >> n >> k;
        long long totaldis = 0;
        for (int i = 0; i < n - 1; i++)
        {
            scanf("%d%d%d", &edge;[i].u, &edge;[i].v, &edge;[i].w);
            totaldis += edge[i].w;
        }
        memset(machine, 0, sizeof(machine));
        for (int i = 0; i < k; i++)
        {
            int temp;
            scanf("%d", &temp;);
            machine[temp] = true;
        }
        cout << totaldis - kruskal(n - 1) << endl;
    }
    return 0;
}

1009 Power transmission HDU4318 送电,每个边都有一个损耗的百分比,经过之后就会损失一些电能,每个点不能重复路过,从起始点到终点求一条损耗最小的路。 刚开始还搜索。。显然会TLE,后来xw有个小错误没写完。Dijkstra最短路,dist数组里存走到某个点剩下的百分比,每次选最大的。


#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 50010;
double dis[MAXN][55];
int edge[MAXN][55];
int t;
bool used[MAXN];
double dist[MAXN];

int main()
{
    int n;
    int s;
    double m;
    while (cin >> n)
    {
        memset(dist, 0, sizeof(dist));
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &edge;[i][0]);
            int temp;
            for (int j = 1; j <= edge[i][0]; j++)
            {
                scanf("%d%d", &edge;[i][j], &temp;);
                dis[i][j] = 1 - temp * 0.01;
            }
        }
        cin >> s >> t >> m;
        dist[s] = 1;
        memset(used, 0, sizeof(used));
        int nodenow;
        double mindis;
        for (int i = 1; i <= n; i++)
        {
            mindis = 0;
            for (int j = 1; j <= n; j++) if (!used[j] && dist[j] > mindis)
            {
                mindis = dist[j];
                nodenow = j;
            }
            if (fabs(mindis) < 1e-6) break;
            used[nodenow] = true;
            if (nodenow == t) break;
            for (int j = 1; j <= edge[nodenow][0]; j++)
            {
                int newnode = edge[nodenow][j];
                double temp = dist[nodenow] * dis[nodenow][j];
                if (!used[newnode] && temp > dist[newnode]) dist[newnode] = temp;
            }
        }
        if (used[t]) printf("%.2lf\n", m * (1 - dist[t]));
        else puts("IMPOSSIBLE!");
    }
    return 0;
}


comments powered by Disqus