题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3339
题意:n个点,m条边,不同边边权不同。除此之外,每个点有了新的 “点权”。设"点权"总和sum,让你求取到一半的"点权"时所需要的最小边权和。
先求出0到每个点的最短路。然后跑一个01背包就可以了。
#include <bits/stdc++.h>
using namespace std;
const int maxn=1E5+5;
const int INF=1<<30;
bool vis[maxn];
int head[maxn];
int d[maxn];
int dp[maxn];
int v[maxn];
int cnt,n,m;
struct node
{
int to,val,next;
}qwe[maxn<<1];
void operation (int a,int b,int c)
{
qwe[cnt].next=head[a];
qwe[cnt].to=b;
qwe[cnt].val=c;
head[a]=cnt++;
}
void reset()
{
memset(vis,false, sizeof(vis));
memset (head,-1, sizeof(head));
memset (dp,0, sizeof(dp));
memset(v,0, sizeof(v));
for (int i=1;i<=n;i++)
d[i]=INF;
cnt=1;
}
void SPFA(int start)
{
int tmp;
vis[start]=true;
d[start]=0;
queue<int>q;
q.push(start);
while (!q.empty())
{
tmp=q.front();
q.pop();
vis[tmp]=false;
for (int i=head[tmp];~i;i=qwe[i].next)
{
if (d[qwe[i].to]>qwe[i].val+d[tmp])
{
d[qwe[i].to]=qwe[i].val+d[tmp];
if (vis[qwe[i].to]==false)
{
q.push(qwe[i].to);
vis[qwe[i].to]=true;
}
}
}
}
}
int main ()
{
int a,b,c,sum,flag,bag;
int t;
scanf ("%d",&t);
while (t--)
{
bag=flag=sum=0;
scanf ("%d%d",&n,&m);
reset();
for (int i=1;i<=m;i++)
{
scanf ("%d%d%d",&a,&b,&c);
operation(a,b,c);
operation(b,a,c);
}
SPFA(0);
for (int i=1;i<=n;i++)
{
scanf ("%d",&v[i]);
sum+=v[i];
bag+=d[i];
}
for (int i=1;i<=n;i++)
for (int j=bag;j>=d[i];j--)
dp[j]=max(dp[j],dp[j-d[i]]+v[i]);
for (int i=1;i<=bag;i++)
if (dp[i]>sum/2)
{
flag=i;
break;
}
if (flag==0)
printf ("impossible\n");
else
printf ("%d\n",flag);
}
return 0;
}