题目链接:点击打开链接
由于最近又学习了一下最短路,开始切了几道题。
有很多人,估计一开始的都会理解错题意,这题的题意是,在0点有n多的tank,我们需要摧毁一些电厂,电厂有一定的电力,总体摧毁一半以上才算成功,否则就是失败。
如果摧毁成功的话,就输出最小的耗油量。
这道题竟然是最短路+01背包,好给力,唯一的遗憾就是没有想到,自己一开始也是有那么一点的一闪而过的念头,但是就是一闪,没有深入想。
一开始以为是,在最长路下的最短路。我擦,现在都不知道自己会有这么一个高端的想法,但是也没有搞出来。
后来,搜到了别人的想法,果然就是一个最短路+01背包。自己曾经有那么一闪而过的想法。真会是正确的想法。这个问题很值得深思啊。
哎。不说了,直接代码吧:
状态方程为:
dp[i][j]=max(dp[i][j],dp[i-1][j-energy[i])表示的是毁掉前i个电厂,花费j的油,最多可以摧毁的能量。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int INF=0xffffff;
const int N=105;
bool vis[N];
int map[N][N],dis[N],power[N],energy[N];
int n,m,sum;
void Dijkstra(){
memset(vis,0,sizeof(vis));
memset(power,0,sizeof(power));
for(int i=1;i<=n;i++){
dis[i]=map[0][i];
}
dis[0]=0;vis[0]=true;
//--------------------------
for(int i=1;i<=n;i++){
int Bfind=0,tmp=INF;
for(int j=0;j<=n;j++){
if(!vis[j]&&dis[j]<tmp){
Bfind=j;tmp=dis[j];
}
}
//------------------------
if(Bfind==0) break;
vis[Bfind]=true;
for(int j=1;j<=n;j++){
if(!vis[j]&&dis[j]>dis[Bfind]+map[Bfind][j]){
dis[j]=dis[Bfind]+map[Bfind][j];
}
}
//-------------------------
}
}
void Init(){
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++)
if(i==j) map[i][j]=0;
else map[i][j]=INF;
}
}
void Zreo_OnePack(){
bool flag[N];
int sumv=0;
memset(flag,0,sizeof(flag));
for(int i=1;i<=n;i++){
if(dis[i]>=INF){
continue;
}
sumv+=dis[i];flag[i]=true;
}
int dp[N*N+5];//到底应该开多大的空间?
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
if(flag[i])
for(int v=sumv;v>=dis[i];v--)
dp[v]=max(dp[v],dp[v-dis[i]]+energy[i]);
}
int ans=-1;
for(int i=1;i<=sumv;i++){
if(dp[i]>sum/2) {
ans=i;
break;
}
}
if(ans==-1) printf("impossible\n");
else printf("%d\n",ans);
}
int main(){
// freopen("1.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
Init();
for(int i=1;i<=m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if(map[a][b]>c){//重边.
map[a][b]=c;map[b][a]=c;
}
}
sum=0;
for(int i=1;i<=n;i++){
scanf("%d",&energy[i]);
sum+=energy[i];
}
energy[0]=0;
Dijkstra();
Zreo_OnePack();
}
return 0;
}
今天晚上有学长讲查分约束,正好和这段的学习有关。恩,先看看吧。。