题意是有人有n个能量站,需要你关闭其中的一部分能量站,使总能量少于一半就可以避免爆炸,给出一些线路的消耗,以及站点的能量值,求最小消耗。
思路:先求出0到达其他所有点最短路,之后就简单01背包就行了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define N 105
#define inf 0x7ffffff
using namespace std;
int n,m,num[N],map[N][N],index[N],dp[10005];
bool vis[N],flag;
void dijkstra()
{
int next,MIN;
num[0]=0;
for(int i=0;i<=n;i++)
{
MIN=inf;
for(int j=0;j<=n;j++)
{
if(!vis[j] && MIN>num[j])
MIN=num[next=j];
}
if(MIN==inf) break;
vis[next]=true;
for(int j=0;j<=n;j++)
{
if(!vis[j] && num[j]>num[next]+map[next][j])
num[j]=num[next]+map[next][j];
}
}
}
int main()
{
int t;
int p,q,r;
scanf("%d",&t);
while(t--)
{
scanf("%d %d",&n,&m);
memset(dp,0,sizeof(dp));
for(int i=0;i<=n;i++)
{
num[i]=inf;
vis[i]=false;
for(int j=0;j<=n;j++)
{
map[i][j]=inf;
}
}
for(int i=0;i<m;i++)
{
scanf("%d %d %d",&p,&q,&r);
if(r<map[p][q])
map[p][q]=map[q][p]=r;
}
int count=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&index[i]);
count+=index[i];
}
dijkstra();
int sum=0;
for(int i=1;i<=n;i++)
{
if(num[i]!=inf)
sum+=num[i];
}
for(int i=1;i<=n;i++)
{
if(num[i]==inf) continue;
for(int j=sum;j>=num[i];j--)
dp[j]=max(dp[j],dp[j-num[i]]+index[i]);
}
count= count/2+1;
flag=false;
int i;
for(i=0;i<=sum;i++)
if(dp[i]>=count)
{
flag=true;
break;
}
if(flag)
printf("%d\n",i);
else printf("impossible\n");
}
return 0;
}