杭电3339————最短路(SPFA)+ 01背包

题目链接:点击打开链接


题目大致如下。

每一个核电站都有一定的电力值,你需要控制这些核电站,使得你控制的电力值的大小超过所有核电站电力值的一半,在保证这个前提的基础上,寻找最短路。

控制核电站,需要一些坦克,坦克的数量是无限的(!注意这个地方!)


一开始的思路:

做一个如下的结构体:

typedef struct{
    int dist;//存取s源点到这个核电站的最短路
    int power;//存取s源点到这个核电站的总power值
}Node;

跑一遍spfa找到最短路的同时,记录最短路经过的点的总电力值。然后将dist数组从小到大排序,只要到某一个站点的电力值超过一半就保证了最短路且控制了电网,直接输出就好了。

然后就WA了。

我的这种思路试用于至于一辆坦克,走一遍的那种,但实际上我们拥有多辆坦克,那么就不可以这么做了。

在看了别人的解答之后,想明白了这个题目在求出源点到所有结点的基础上,用一次01背包就可以解决问题。

稍微解释一下为什么01背包:

对于每一个站点,我们可以选择占领或者不占领。占领一个站点需要花费的费用为到该点的最短路,获得的价值即为该点的电力值。

这样一说,很显然是01背包的思路:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <queue>
#define maxn 105
#define INF 0x7ffffff
using namespace std;

int dist[maxn];
int G[maxn][maxn];
int n,m;
int power[maxn],total_power;
int dp[10010];//注意这个数组的大小
queue<int> Q;

int Min(int x,int y)
{
    return (x < y) ? x : y;
}
int Max(int x,int y)
{
    return (x > y) ? x : y;
}
void init()
{
    for(int i =  0 ; i < maxn ; ++i){
        for(int j = 0 ; j < maxn ; ++j){
            if(i == j)
                G[i][j] = 0;
            else
                G[i][j] = INF;
        }
    }
    memset(power,0,sizeof(power));
    memset(dp,0,sizeof(dp));
    total_power = 0;

}
void SPFA()
{
    bool inq[maxn];//inqueue

    memset(inq,false,sizeof(inq));
    for(int i = 0 ; i <= n ; ++i)
        dist[i] = INF;
    while(!Q.empty())
        Q.pop();//每次SPFA前清空队列
    Q.push(0);
    inq[0] = true,dist[0] = 0;
    while(!Q.empty())
    {
        int now = Q.front();
        Q.pop();
        inq[now] = false;
        for(int next = 0 ; next <= n ; ++next)
        {
            if(G[now][next] != INF)//连通的
            {
                if(dist[now] + G[now][next] < dist[next])//relax
                {
                    dist[next] = dist[now] + G[now][next];
                    if(!inq[next])
                    {
                        inq[next] = true;
                        Q.push(next);
                    }
                }
            }
        }
    }
}
int main()
{
    int casenum;
    int v1,v2,weight,half;

    cin >> casenum;
    while(casenum--){
        cin >> n >> m;
        init();
        while(m--){
            cin >> v1 >> v2 >> weight;
            G[v2][v1] = G[v1][v2] = Min(G[v1][v2],weight);
        }
        for(int i = 1 ; i <= n ; ++i){//读取power值
            cin >> power[i];
            total_power += power[i];
        }
        half = total_power / 2;
        weight = 0;
        SPFA();
        for(int i = 1 ; i <= n ; ++i)
            if(dist[i] != INF)
                weight += dist[i];
        for(int i = 1 ; i <= n ; ++i)//对于每一个站点
            for(int j = weight ; j >= dist[i] ; --j)
                dp[j] = Max(dp[j],dp[j-dist[i]]+power[i]);
        int flag = -1;
        for(int i = 0 ; i <= weight ; ++i){
            if(dp[i] > half){
                flag = i;
                break;
            }
        }
        if(flag == -1)
            cout << "impossible" << endl;
        else
            cout << flag << endl;
    }
    return 0;
}

附上之前的错误代码:

(思路还好,只不过题目理解错了)

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <queue>
#define maxn 105
#define INF 0x7ffffff
using namespace std;

typedef struct{
    int dist;//存取s源点到这个核电站的最短路
    int power;//存取s源点到这个核电站的总power值
}Node;

Node node[maxn];
int G[maxn][maxn];
int n,m;
int power[maxn],total_power;
queue<int> Q;

int Min(int x,int y)
{
    return (x < y) ? x : y;
}
void init()
{
    for(int i =  0 ; i < maxn ; ++i)
        for(int j = 0 ; j < maxn ; ++j)
            G[i][j] = INF;
    memset(power,0,sizeof(power));
    total_power = 0;
}
void SPFA()
{
    bool inq[maxn];//inqueue

    memset(inq,false,sizeof(inq));
    for(int i = 0 ; i <= n ; ++i){
        node[i].dist = INF;
        node[i].power = 0;
    }
    while(!Q.empty())
        Q.pop();//每次SPFA前清空队列
    Q.push(0);
    inq[0] = true,node[0].dist = 0;
    while(!Q.empty())
    {
        int now = Q.front();
        Q.pop();
        inq[now] = false;
        for(int next = 0 ; next <= n ; ++next)
        {
            if(G[now][next] != INF)//连通的
            {
                if(node[now].dist + G[now][next] < node[next].dist)//relax
                {
                    node[next].dist = node[now].dist + G[now][next];
                    node[next].power = node[now].power + power[next];//这个地方很重要啊
                    if(!inq[next])
                    {
                        inq[next] = true;
                        Q.push(next);
                    }
                }
            }
        }
    }
}
int cmp(const void *a,const void *b)
{
    Node *A = (Node*)a;
    Node *B = (Node*)b;

    return (A->dist > B->dist);
}
int main()
{
    int casenum;
    int v1,v2,weight,half;

    cin >> casenum;
    while(casenum--){
        cin >> n >> m;
        init();
        while(m--){
            cin >> v1 >> v2 >> weight;
            G[v2][v1] = G[v1][v2] = Min(G[v1][v2],weight);
        }
        for(int i = 1 ; i <= n ; ++i){//读取power值
            cin >> power[i];
            total_power += power[i];
        }
        SPFA();
        qsort(node,n,sizeof(Node),cmp);
        if(total_power % 2 == 0)
            half = total_power / 2 + 1;
        else
            half = (total_power + 1) / 2;
        int flag = -1;
        for(int i = 1 ; i <= n ; ++i){
            if(node[i].power >= half && node[i].dist != INF){
                flag = i;
                break;
            }
        }
        if(flag == -1)
            cout << "impossible" << endl;
        else
            cout << node[flag].dist << endl;

    }
    return 0;
}





posted @ 2014-11-26 20:14  SixDayCoder  阅读(192)  评论(0编辑  收藏  举报