http://acm.hdu.edu.cn/showproblem.php?pid=3339
大致题意是说:
有n个电站,每个电站都有一定的电量,电站之间有一定距离,我们要从0点出发去占领一些电站,使得占领的电站电量之和超过总电量的一半,求达到条件所要走的最短距离。如果可能的话,输出距离,否则输出不可能。
思路:
我们从0点开始派出一些tank去占领一些电站,坦克到每个电站都有一定距离,而占领每个电站之后可以得到一定电量,距离就相当于体积v,电量就相当于价值w,这不是就01背包吗?01背包通常的问法是给定体积,求获得最大的价值,这里的问法是给定价值,求恰好得到或多于该价值时的最小体积。我们只要从前向后搜索,找到第一个大于该价值的体积即可。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int INF = 0x7fffff;
const int N = 110;
int G[N][N],power[N],dis[N],vis[N],dp[N*N];
int n,m;
void dijkstra(int scr){
for(int i=1; i<=n; i++){
dis[i] = G[scr][i];
vis[i] = 0;
}
vis[scr] = 1;
for(int i=1; i<n; i++){
int tmp = INF, k = scr;
for(int j=1; j<=n; j++){
if(vis[j]) continue;
if(dis[j] < tmp){
tmp = dis[j];
k = j;
}
}
if(tmp == INF) return;
vis[k] = 1;
for(int j=1; j<=n; j++){
if(vis[j]) continue;
dis[j] = min(dis[j], dis[k] + G[k][j]);
}
}
}
int main(){
// freopen("in.txt", "r", stdin);
int cas;
scanf("%d",&cas);
while(cas--){
scanf("%d%d",&n,&m);
for(int i=0; i<=n; i++)
for(int j=0; j<=n; j++){
if(i == j) G[i][j] = 0;
else G[i][j] = INF;
}
int x,y,d;
for(int i=0; i<m; i++){
scanf("%d%d%d",&x,&y,&d);
if(d < G[x][y])///防止重边
G[x][y] = G[y][x] = d;
}
for(int i=1; i<=n; i++)
scanf("%d",&power[i]);
dijkstra(0);
int v = 0, w = 0, flag = 1;
for(int i=1; i<=n; i++){
w += power[i];
v += dis[i];
if(dis[i] == INF){
flag = 0;
break;
}
}
if(!flag){
puts("impossible");
continue;
}
for(int i=0; i<=v; i++)
dp[i] = 0;
for(int i=1; i<=n; i++){
for(int j=v; j>=dis[i]; j--){
dp[j] = max(dp[j], dp[j - dis[i]] + power[i]);
}
}
w = w/2 + 1;
for(int i=1; i<=v; i++){
if(dp[i] >= w){
printf("%d\n",i);
break;
}
}
}
return 0;
}