杭电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; }