hdu3339 In Action(Dijkstra+01背包)

/*
   题意:有 n 个站点(编号1...n),每一个站点都有一个能量值,为了不让这些能量值连接起来,要用
   坦克占领这个站点!已知站点的 之间的距离,每个坦克从0点出发到某一个站点,1 unit distance costs 1 unit oil!
   最后占领的所有的站点的能量值之和为总能量值的一半还要多,问最少耗油多少!

*/

/*
     思路:不同的坦克会占领不同的站点,耗油最少那就是路程最少,所以我们先将从 0点到其他各点的
     最短距离求出来!也就是d[i]的值!然后我们又知道每一个站点的所具有的能量值!也就是w[i];
     最后求出满足占领站点的能量比总能量的一半多并且路程最少。。。直接01背包走起!
*/
#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#define N 10005
#define INF 0x3f3f3f3f
using namespace std;

int w[105];

struct EDGE{
   int u, v, nt, dist;
   EDGE(){}

   EDGE(int u, int v, int nt, int dist){
      this->u=u;
      this->v=v;
      this->nt=nt;
      this->dist=dist;
   }
};

EDGE edge[N*2];
int first[105];
int cnt;
queue<pair<int, int> >q;
int n, m;
int dp[10005];
int d[105];
int map[105][105];

void addEdge(int u, int v, int dist){
    edge[cnt++]=EDGE(u, v, first[u], dist);
    first[u]=cnt-1;
    edge[cnt++]=EDGE(v, u, first[v], dist);
    first[v]=cnt-1;
}

void Dijkstra(){
   d[0]=0;
   q.push(make_pair(0, 0));
   while(!q.empty()){
       pair<int,int> cur=q.front();
       q.pop();
       int u=cur.second;
       if(d[u] != cur.first) continue;
       for(int e=first[u]; e!=-1; e=edge[e].nt){
              int v=edge[e].v, dist=edge[e].dist;
              if(d[v] > d[u] + dist){
                 d[v] = d[u] + dist;
                 q.push(make_pair(d[v], v));
              }
       }
   }
}

int main(){
   int t;
   int sumP, sumD;
   scanf("%d", &t);
   while(t--){
      scanf("%d%d", &n, &m);
      cnt=0;
      memset(d, 0x3f, sizeof(d));
      memset(first, -1, sizeof(first));
      for(int i=0; i<=n; ++i)
         for(int j=0; j<=n; ++j)
            map[i][j]=INF;
      while(m--){
          int u, v, dist;
          scanf("%d%d%d", &u, &v, &dist);
          if(map[u][v]>dist)
              map[u][v]=map[v][u]=dist;
      }
      for(int i=0; i<=n; ++i)
         for(int j=0; j<=i; ++j)
            if(map[i][j]!=INF)
              addEdge(i, j, map[i][j]);
      Dijkstra();//求出 0点到其他个点的最短的距离!
      sumP=sumD=0;
      for(int i=1; i<=n; ++i){
         scanf("%d", &w[i]);
         sumP+=w[i];
         sumD+=d[i];
      }
      memset(dp, 0x3f, sizeof(dp));//初始背包的总价值为无穷大
      dp[0]=0;

      //zeroOnePackage... d[i]相当于价值(也就是耗油量), w[i]相当于容积(也就是能量值)
      for(int i=1; i<=n; ++i)
         for(int j=sumP; j>=w[i]; --j)
            dp[j]=min(dp[j], dp[j-w[i]]+d[i]);

      int maxCost=INF;
      for(int i=sumP/2+1; i<=sumP; ++i)//注意是sumP/2+1(因为要比一半多)
            if(maxCost>dp[i])
               maxCost=dp[i];
      if(maxCost==INF)
         printf("impossible\n");
      else printf("%d\n", maxCost);
   }
   return 0;
}

/*
   思路:dp[i][j]表示到达 i站点, 并且占领的能量值为 j时的耗油最小值!
         开始想用的是spfa算法,并且在进行节点之间距离松弛的时候,也将       背包融进来,但是超时啊!
         好桑心.....
*/

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#define N 10005
#define INF 0x3f3f3f3f
using namespace std;

int w[105];

struct EDGE{
   int u, v, nt, dist;
   EDGE(){}

   EDGE(int u, int v, int nt, int dist){
      this->u=u;
      this->v=v;
      this->nt=nt;
      this->dist=dist;
   }
};

EDGE edge[N*2];
int first[105];
int cnt;
queue<pair<int, int> >q;
int vis[105];
int n, m, sum;
int dp[105][10005];
int map[105][105];

void addEdge(int u, int v, int dist){
    edge[cnt++]=EDGE(u, v, first[u], dist);
    first[u]=cnt-1;
    edge[cnt++]=EDGE(v, u, first[v], dist);
    first[v]=cnt-1;
}

void spfa(){
   dp[0][0]=0;
   q.push(make_pair(0, 0));
   vis[0]=1;
   while(!q.empty()){
       pair<int,int> cur=q.front();
       q.pop();
       int u=cur.second;
       vis[u]=0;
       for(int e=first[u]; e!=-1; e=edge[e].nt){
           int v=edge[e].v, dist=edge[e].dist;
           for(int j=w[v]; j<=sum; ++j)
              if(dp[v][j] > dp[u][j-w[v]] + dist){
                 dp[v][j] = dp[u][j-w[v]] + dist;
                 if(!vis[v]){
                    vis[v]=1;
                    q.push(make_pair(dp[v][j], v));
                 }
              }
       }
   }
}

int main(){
   int t;
   scanf("%d", &t);
   while(t--){
      scanf("%d%d", &n, &m);
      cnt=0;
      memset(first, -1, sizeof(first));
      for(int i=0; i<=n; ++i)
         for(int j=0; j<=n; ++j)
            map[i][j]=INF;
      while(m--){
          int u, v, dist;
          scanf("%d%d%d", &u, &v, &dist);
          if(map[u][v]>dist)
              map[u][v]=map[v][u]=dist;
      }
      for(int i=0; i<=n; ++i)
         for(int j=0; j<=n; ++j)
            if(map[i][j]!=INF)
              addEdge(i, j, map[i][j]);
      for(int i=1; i<=n; ++i){//最后将1...n节点的最优值汇聚到 第 n+1个节点上
           edge[cnt++]=EDGE(i, n+1, first[i], 0);
           first[i]=cnt-1;
      }
      sum=0;
      for(int i=1; i<=n; ++i){
         scanf("%d", &w[i]);
         sum+=w[i];
      }
      w[n+1]=0;
      for(int i=0; i<n+2; ++i)
         for(int j=0; j<sum+2; ++j)
            dp[i][j]=INF;
      spfa();
      int maxCost=INF;
      for(int i=sum/2+1; i<=sum; ++i)
            if(maxCost>dp[n+1][i])
               maxCost=dp[n+1][i];
      if(maxCost==INF)
         printf("impossible\n");
      else printf("%d\n", maxCost);
   }
   return 0;
}
时间: 2024-08-16 01:01:52

hdu3339 In Action(Dijkstra+01背包)的相关文章

UVa 562 Dividing coins (0-1背包&amp;amp;等价转化)

562 - Dividing coins Time limit: 3.000 seconds http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=503 It's commonly known that the Dutch have invented copper-wire. Two Dutch men were

算法:poj 3628 Bookshelf 2(dfs, 01背包)

题目大意: 有n个数字(1-100W),现在有一个数b,1<=b<=s(s为n个数字之和).要从n 个数字中选择一些数字组合,有一写组合的数字之和x会大于等于b, 求所有x中x-b最小的是多少? 思路: 刚开始看到这题,发现数字这么大,以为内存不够不能用背包.而n最大才20,所以直接用 dfs+减枝0MS过了... 然后用背包,开了2000W+的数组,memset初始化,果断地MLE了...然后 看了下discuss,发现用memset会增加很多额外的内存. 于是改用for循环初始化,内存就够

算法:poj 3211 Washing Clothes(01背包)

题目大意: Dearboy和他的女朋友一起洗m件衣服,共有n总颜色(每件衣服只有一种颜色). 为 了放置不同颜色互染,每次只能洗一种颜色的衣服,在这件衣服洗完之前不能洗另外一种衣服. Dearboy和 他女朋友可以同时一起洗衣服,但是不能同时洗同一件衣服,也不能同时洗不同种颜色的衣服. 一 只每件衣服所需时间,问最少花费多少时间可以全部洗完. 分析: 首先可以很直观的判定, 一定是把一种颜色的所有衣服都洗完再接着洗另外一种颜色的衣服,洗每种颜色所有衣服的总时间是分开独 立的. 关键是洗同一种颜色

算法:poj 2923 Relocation (枚举+背包 | 状态压缩+01背包)

题目大意: 有N件家具,每件的重量为(1 ≤ wi ≤ 100), 用两辆车把他们来运送到目的地.这 两辆车的限载重量分别为C1, C2(1 ≤ Ci ≤ 100) , 问最少几趟可以把所有家具运送到目的地. 这两辆车每趟都必须一起走,即使某辆车没载东西. 思路: (一) 先上自己的方法: 枚举第一辆车可能载的家具的所有组合情况,那么用二进制来表示状态则共有 1<<n ,如果 二进制的第i位上是1,那么就表示第i件家具是由第一辆车运的. 这样就相当于把所有家具分成了两 组,一组给第一辆车运,另

算法:POJ 2184 Cow Exhibition (dp 转换01背包)

题目大意: 有N个物品,每个物品有属性Si和Fi,-1000 <= Si, Fi <= 1000, 每种物品最 多只能选一次,问怎样选使得物品的所有Si和Fi属性之和最大,并且要求Si之和与Fi之和都不能下于 0. 思路: 这题想了很久都没思路,于是跟前辈请教了下,恍然大悟.把属性Si当做是物品的 费用,Fi当做是价值,然后做01背包即可. 代码: #include<iostream> #include<queue> #include<stack> #inc

算法:hdu 2639 Bone Collector II (dp 01背包求第k优解)

题目大意: 有n件物品,每件物品有体积和价值两个属性, 一个小偷带着一个大小为v的背包,要 偷这些东西,问小偷能偷的第k大的价值是多少? 思路: 这题和典型的01背包求最优解不同, 是要求第k大的解,所以,最直观的想法就是在01背包的基础上再增加一维,用来保存前k大小的数,然后在 递推时,根据前一个状态的前k 大小的数推出下一个阶段的前k个数保存下来. d[i][j][k], 表示取前i个物品,用j的费用,第k大价值是多少 在递推d[i][j][1...k]时,先获取上一个状态d[i- 1][j

优化-01背包回溯法计算起来非常慢,有木有算法大大帮忙看看

问题描述 01背包回溯法计算起来非常慢,有木有算法大大帮忙看看 #include #include #include #include #include #include using namespace std; int n;//物品数量 double c;//背包容量 double v[9000];//各个物品的价值 double w[9000];//各个物品的重量 double cw = 0.0;//当前背包重量 double cp = 0.0;//当前背包中物品价值 double best

0-1背包-poj-1948-Triangular Pastures

poj-1948-Triangular Pastures 携程大赛2014.4.11 预赛第二场第二题 Description Like everyone, cows enjoy variety. Their current fancy is new shapes for pastures. The old rectangular shapes are out of favor; new geometries are the favorite.  I. M. Hei, the lead cow 

01背包相关问题c语言算法设计课程设计

问题描述 01背包相关问题c语言算法设计课程设计 一.设计题目 0/1背包问题 问题描述:一个商人带着一个能够装m千克的背包去乡下收购货物,准备将这些货物卖的城里获利.现有n种货源,且知道第i种货物有Wi千克,可获得Pi元,收购那些货物以获得最大利润.(在选择装入背包时,对每种货物只有两种选择,即装入背包,或不装入背包,货物不允许拆零) 二.设计主要内容 具体要求如下: (1) 使用蛮力算法实现 (2) 使用递归算法实现 (3) 使用动态规划算法实现 (4) 对各种算法的时间复杂度进行分析和比较