生成树&最短路总结篇

1、模板题  我是用prim搞得 给出每点坐标求最小生成树

hdu1162Eddy‘s picture 最小生成树

#include <iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int flag1=0;
double sum;
double arr_list[110][110];
struct Edge
{
     int point;
     double lowcost;
     int flag;
};
Edge edge[12100];
struct Point
{
     double x,y;
}point[110];
double prim(int n)
{
     int i,j,k=1,flag;
     double min,sum2=0;
     j=1;
     for(i=1;i<=n;i++)
     {
          if(i!=j)
          {
               edge[i].point=i;
               edge[i].lowcost=arr_list[j][i];
               edge[i].flag=0;
          }
     }
     edge[j].lowcost=0;
     edge[j].flag=1;
     for(i=2;i<=n;i++)
     {
          k=1;
          min=65535;
          flag=0;
          for(j=2;j<=n;j++)
          {
               if(edge[j].flag==0&&edge[j].lowcost<min)
               {
                    k=j;
                    min=edge[j].lowcost;
                    flag=1;
               }
          }
          if(!flag) return -1;
          sum2+=min;
          edge[k].flag=1;
          for(j=2;j<=n;j++)
          {
               if(edge[j].flag==0&&arr_list[k][j]<edge[j].lowcost)
               {
                    edge[j].point=k;
                    edge[j].lowcost=arr_list[k][j];
               }
          }
     }
     return sum2;
}
int main()
{
   // freopen("cin.txt","r",stdin);
    int n;
    while(~scanf("%d",&n))
    {
         for(int i=1;i<=n;i++)
         {
              cin>>point[i].x>>point[i].y;
              arr_list[i][i]=65535;
         }
         for(int i=1;i<n;i++)
         {
              for(int j=i+1;j<=n;j++)
              {
                   arr_list[i][j]=sqrt(pow((point[i].x-point[j].x),2)+pow((point[i].y-point[j].y),2));
                   arr_list[j][i]=arr_list[i][j];
                   //cout<<arr_list[i][j]<<endl;
              }
         }
         sum=prim(n);
         printf("%.2lf\n",sum);
    }
    return 0;
}

2、模板题 floyd最短路相加改成相乘

hdu1596find the safest road最短路floyd

    #include <iostream>
    #include<cstdio>
    using namespace std;
    double dist[1005][1005];
    int main()
    {
      //  freopen("cin.txt","r",stdin);
        int n,q,a,b;
        while(~scanf("%d",&n))
        {
             for(int i=0;i<n;i++)
             {
                  for(int j=0;j<n;j++)
                  {
                       scanf("%lf",&dist[i][j]);
                  }
             }
             for(int i=0;i<n;i++)
             {
                  for(int j=0;j<n;j++)
                  {
                       for(int k=0;k<n;k++)
                       {
                            if(dist[j][i]*dist[i][k]>dist[j][k])
                             dist[j][k]=dist[j][i]*dist[i][k];
                       }
                  }
             }
             scanf("%d",&q);
             while(q--)
             {
                  scanf("%d%d",&a,&b);
                  a--;b--;
                  if(dist[a][b]>0.000001)
                  printf("%.3lf\n",dist[a][b]);
                  else puts("What a pity!");
             }
        }
        return 0;
    }  

3、稍微转弯的prim

poj2349Arctic Network最小生成树

题意:p个哨所间只有s个卫星通道,但每个哨所都有无线发射器,无线发射的功率随长度增加而增加但是每个都一样,问最小长度?

我们知道prim算法中的步骤是每次加入最小的边,但是并没有将加入的边存储下来,加一个数组存储下来,倒序排序,前s个边用卫星通道就好啦

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int flag1=0;
    int n,s,p;
    double sum;
    double arr_list[504][504];
    double d[503];
    struct Edge
    {
         int point;
         double lowcost;
         int flag;
    };
    Edge edge[505];
    bool cmp(double a,double b)
    {
         return a>b;
    }
    struct Point
    {
         double x,y;
    }point[502];
    void prim(int p)
    {
         int i,j,k=1;
         double min,sum2=0;
         j=1;
         for(i=1;i<=p;i++)
         {
              if(i!=j)
              {
                   edge[i].point=j;
                   edge[i].lowcost=arr_list[j][i];
                   edge[i].flag=0;
              }
         }
         edge[j].lowcost=0;
         edge[j].flag=1;
         int l=0;
         for(i=1;i<p;i++)
         {
              min=65535000;
              for(j=2;j<=p;j++)
              {
                   if(edge[j].flag==0&&edge[j].lowcost<min)
                   {
                        k=j;
                        min=edge[j].lowcost;
                   }
              }
              d[l++]=arr_list[k][edge[k].point];
              //sum2+=min;
              edge[k].flag=1;
              for(j=2;j<=p;j++)
              {
                   if(edge[j].flag==0&&arr_list[k][j]<edge[j].lowcost)
                   {
                        edge[j].point=k;
                        edge[j].lowcost=arr_list[k][j];
                   }
              }
         }
    }
    int main()
    {
          //  freopen("cin.txt","r",stdin);
             cin>>n;
             while(n--)
             {
                   cin>>s>>p;
                   for(int i=1;i<=p;i++)
                   {
                        cin>>point[i].x>>point[i].y;
                        arr_list[i][i]=65535000;
                   }
                   for(int i=1;i<p;i++)
                   {
                        for(int j=i+1;j<=p;j++)
                        {
                             arr_list[i][j]=sqrt(pow((point[i].x-point[j].x),2)+pow((point[i].y-point[j].y),2));
                             arr_list[j][i]=arr_list[i][j];
                       //cout<<arr_list[i][j]<<endl;
                        }
                   }
                   prim(p);
                   sort(d,d+p-1,cmp);
                   cout.setf(ios::fixed);//保留两位小数
                   cout.precision(2);
                   cout<<d[s-1]<<endl;
             }
        return 0;
    }  

4、多源点单汇点 之前都是单源最短路,如果是多源单汇点的话也是一样的

hdu2680Choose the best route dijkstra

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    #define MaxN 10010
    #define MaxInt 2000000
    using namespace std;
    int map[MaxN][MaxN],dist[MaxN],start;
    bool mark[MaxN];
    int n,m,s,p,q,t,w,b,end,min1,minn,ww;
    void dijkstra(int s)
    {
         memset(mark,0,sizeof(mark));
         for(int i=1;i<=n;i++) dist[i]=map[s][i];
         mark[s]=1;dist[s]=0;
         int k;
         for(int i=1;i<n;i++)
         {
              minn=MaxInt;
              for(int j=1;j<=n;j++)
              {
                   if(!mark[j]&&minn>dist[j])
                   {
                        k=j;
                        minn=dist[j];
                   }
              }
              if(minn==MaxInt) break;
              mark[k]=1;
              for(int j=1;j<=n;j++)
              {
                   if(!mark[j]&&dist[j]>dist[k]+map[k][j])
                   {
                        dist[j]=dist[k]+map[k][j];
                   }
              }
         }
    }
    int main()
    {
       // freopen("cin.txt","r",stdin);
        while(~scanf("%d%d%d",&n,&m,&s))
        {
             for(int i=1;i<=n;i++)
             {
                  for(int j=1;j<=n;j++)
                  {
                       map[i][j]=MaxInt;
                  }
             }
             for(int i=0;i<m;i++)
             {
                  scanf("%d%d%d",&p,&q,&t);
                  if(t<map[q][p])
                  map[q][p]=t;
             }
             dijkstra(s);
             min1=MaxInt;
             scanf("%d",&w);
             while(w--)
             {
                  scanf("%d",&ww);
                  if(min1>dist[ww]) min1=dist[ww];
             }
             if(min1!=MaxInt)
             printf("%d\n",min1);
             else printf("-1\n");
        }
        return 0;
    }  

5、限定必须连上给定的两点 这是一个关于最小生成树的原理应用,也是以签到题的姿态出现的区域赛题

HDU 4463 Outlets 最小生成树Kr~

既然要求他俩必须先连,那就先连上,逐渐选择最小的边加入边集中就ok啦

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int maxn=60*60;
    struct point
    {
        int x,y;
        double dis;
        point(){}
        point(int _x,int _y,double _d):x(_x),y(_y),dis(_d){}
        bool operator <(const struct point &tmp)const{
            return this->dis<tmp.dis;
        }
    }p[maxn];
    point pp[100];
    int par[60];
    void init(){
        for(int i=0;i<60;i++) par[i]=i;
    }
    int find_par(int x){
        if(x!=par[x]) return par[x]=find_par(par[x]);
        return par[x];
    }
    bool Union(int x,int y){
        x=find_par(x);
        y=find_par(y);
        if(x!=y){
            par[y]=x;
            return true;
        }
        return false;
    }
    double calu(point a,point b){
        return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    int main()
    {
       // freopen("cin.txt","r",stdin);
        int n,p1,q1;
        while(~scanf("%d",&n)&&n)
        {
            init();
            scanf("%d%d",&p1,&q1);
            int cnt=0;
            for(int i=1;i<=n;i++)
            {
                int x,y;
                scanf("%d%d",&pp[i].x,&pp[i].y);
                for(int j=1;j<i;j++)
                {
                    double d=calu(pp[i],pp[j]);
                    p[cnt++]=point(i,j,d);
                }
            }
            double ans=0;
            Union(p1,q1);
            ans+=calu(pp[p1],pp[q1]);
            sort(p,p+cnt);
            for(int i=0;i<cnt;i++)
            {
                if(Union(p[i].x,p[i].y))
                 ans+=p[i].dis;
            }
            printf("%.2lf\n",ans);
        }
    }  

6、floyd最小环 裸题 前一个是输出路径

poj1734Sightseeing trip floyd最小环

hdu1599 find the mincost route

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cstdio>
#include<climits>
#include<algorithm>
using namespace std;

const int N=111;
const int INF=0xffffff;

int min_loop;
int num;
int map[N][N],dist[N][N],pre[N][N];
int path[N];
int n,m;

void dfs(int i,int j)
{
    int k=pre[i][j];
    if(k==0)
    {
        path[num++]=j;
        return;
    }
    dfs(i,k);
    dfs(k,j);
}

void Floyd()
{
    min_loop=INF;
    memset(pre,0,sizeof(pre));
    for(int k=1; k<=n; k++)
    {
        for(int i=1; i<k; i++)
        {
            for(int j=i+1; j<k; j++)
            {
                if(dist[i][j]+map[i][k]+map[k][j]<min_loop)
                {
                    min_loop=dist[i][j]+map[i][k]+map[k][j];
                    num=0;
                    path[num++]=i;
                    dfs(i,j);
                    path[num++]=k;
                }
            }
        }

        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                if(dist[i][k]+dist[k][j]<dist[i][j])
                {
                    dist[i][j]=dist[i][k]+dist[k][j];
                    pre[i][j]=k;
                }
            }
        }
    }
}

int main()
{
 //   freopen("cin.txt","r",stdin);
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1; i<=n; i++)
        {
            for(int j=i+1; j<=n; j++)
                map[i][j]=map[j][i]=dist[i][j]=dist[j][i]=INF;
            map[i][i]=dist[i][i]=0;
        }
        for(int i=0; i<m; i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            if(w<map[u][v])
            {
                map[u][v]=map[v][u]=w;
                dist[u][v]=dist[v][u]=w;
            }
        }
        Floyd();
        if(min_loop==INF)
            puts("No solution.");
        else
        {
            for(int i=0; i<num-1; i++)  printf("%d ",path[i]);
            printf("%d\n",path[num-1]);
           //printf("%d\n",min_loop);
        }
    }
    return 0;
}

7、单向最短路来回最短距离

POJ 3268 Silver Cow Party

虽说是来回都得求值,还记不记得之前的“孙大神的山峰序列” 一样的道理,从两边求完之后遍历数组求和最小即为答案

    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    #include<cstdio>
    using namespace std;
    int n,m,x;
    const int  MAXINT=0x3f3f3f3f;
    const int MAXNUM=1100;
    int dist[MAXNUM];
    int A[MAXNUM][MAXNUM];
    void Dijkstra(int v0)
    {
        bool S[MAXNUM];// 判断是否已存入该点到S集合中
        for(int i=1; i<=n; ++i)
        {
            dist[i]=A[v0][i];
           // printf("%d    ",dist[i]);
            S[i]=false; // 初始都未用过该点
        }
       // dist[v0] = 0;
        S[v0]=true;
        for(int i=2; i<=n; i++)
        {
            int mindist = MAXINT;
            int u = -1;// 找出当前未使用的点j的dist[j]最小值
            for(int j=1; j<=n; ++j)
            if((!S[j]) && dist[j]<mindist)
            {
                u = j;// u保存当前邻接点中距离最小的点的号码
                mindist = dist[j];
                //printf("%d        ",mindist);
            }
            S[u] = true;
            for(int j=1; j<=n; j++)
                if((!S[j]) && A[u][j]<MAXINT)
                {
                    if(dist[u] + A[u][j] < dist[j])     //在通过新加入的u点路径找到离v0点更短的路径
                    {
                        dist[j] = dist[u] + A[u][j];    //更新dis                    //记录前驱顶点
                    }
                }
        }
        return;
    }
    int main()
    {
        //freopen("cin.txt","r",stdin);
        while(~scanf("%d%d%d",&n,&m,&x))
        {
            for(int i=1; i<=n; i++)
            {
                for(int j=i+1; j<=n; j++)
                    A[i][j]=A[j][i]=MAXINT;
            }
            for(int i=0; i<m; i++)
            {
                int u,v,w;
                scanf("%d%d%d",&u,&v,&w);
                A[u][v]=w;
                //printf("%d %d %d\n",u,v,A[u][v]);
            }
            Dijkstra(x);
            int a1[MAXNUM],a2[MAXNUM];
            for(int i=1;i<=n;i++) a1[i]=dist[i];//printf("%d ",a1[i]);
           // puts("");
            for(int i=1;i<=n;i++)
                for(int j=i+1;j<=n;j++)
                {
                    int tmp;
                    tmp=A[i][j];
                    A[i][j]=A[j][i];
                    A[j][i]=tmp;
                }
            Dijkstra(x);
            for(int i=1;i<=n;i++) a2[i]=dist[i];
            int minn=0;
            for(int i=1;i<=n;i++)
            {
                minn=max(minn,a1[i]+a2[i]);
            }
            printf("%d\n",minn);
        }
        return 0;
    }  

8、换货币,给出汇率,问是否可以通过一系列的折腾使得手中的钱增多,其实也不容想到是用floyd最短路的方法写==    dist[i][i]>1.0即为钱数增加

POJ 2240 Arbitrage floyd

    #include <iostream>
    #include <cstdio>
    #include<map>
    #include <string.h>
    using namespace std;
    map<string,int>mat;
    double dist[100][100];
    int n,m;
    void Floyd()
    {
         for(int k=1;k<=n;k++)
            for(int i=1; i<=n; i++)
            {
                for(int j=1; j<=n; j++)
                {
                    if(dist[i][k]*dist[k][j]>dist[i][j])
                    {
                        dist[i][j]=dist[i][k]*dist[k][j];
                       // pre[i][j]=k;
                    }
                }
            }  

    }
    int main()
    {
       // freopen("cin.txt","r",stdin);
        map<string,int>mat;
        int cnt=0;
        double w;
        string str,str1,str2;
        while(~scanf("%d",&n)&&n)
        {
            //mat.clear();
            for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
            {
                if(i==j) dist[i][j]=1;
                else dist[i][j]=0;
            }
            for(int i=1;i<=n;i++) cin>>str,mat[str]=i;
            cin>>m;
            while(m--)
            {
                cin>>str1>>w>>str2;
                dist[mat[str1]][mat[str2]]=w;
                //maap[mat[str1]][mat[str2]]=w;
            }
            Floyd();
            printf("Case %d:",++cnt);
            bool flag=false;
            for(int i=1;i<=n;i++)
                if(dist[i][i]>1.0)
                {
                    flag=true;
                    break;
                }
            if(!flag) printf(" No\n");
            else printf(" Yes\n");
        }
        return 0;
    }  

9、换货币,给出汇率和手续费,问是否可以使得手里钱增加 spfa

POJ 1860 Currency Exchange spfa

同样是换货币,之前的题是只有汇率,这个题还有手续费,spfa  bellman都可做,我当时用的前者。为什么要用这两个方法,因为要判断"负环",当然了这个题存在“钱无限增加”的情况就说明松弛操作可以进行很多次,就是正常函数返回-1的情况==

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <queue>
    #include <string.h>
    using namespace std;  

    const int INF=0x3f3f3f3f;
    const int maxm=511111;
    const int maxn=111111;  

    struct EdgeNode
    {
        int to;
        int next;
        double rate,comm,w;
    };
    int cont[maxn];
    EdgeNode edges[maxm];
    int N,M,s;
    double val;
    int head[maxn],edge;
    bool vis[maxn];
    queue <int> que;
    double dis[maxn];  

    void addedge(int u,int v,double r,double c)
    {
        edges[edge].comm=c,edges[edge].to=v;
        edges[edge].next=head[u];edges[edge].w=0;
        edges[edge].rate=r;head[u]=edge++;
    }  

    void init()
    {
        memset(head,-1,sizeof(head));
        edge=0;
        memset(cont,0,sizeof(cont));
        memset(vis,false,sizeof(vis));
        for (int i=0; i<N; i++)
            dis[i]=0;
    }  

    int spfa(int s,int n)//单源最短路(s为起点,n为节点总数)///
    {
        int u;  

        while (!que.empty()) que.pop();  

        vis[s]=true;
        dis[s]=val;
        ++cont[s];
        que.push(s);
        while (!que.empty())
        {
            u=que.front();
            que.pop();
            vis[u]=false;
            for (int i=head[u]; i!=-1; i=edges[i].next)
            {
                int v=edges[i].to;
                edges[i].w=(dis[u]-edges[i].comm)*edges[i].rate-dis[u];
                if (dis[v]<dis[u]+edges[i].w)///
                {
                    dis[v]=dis[u]+edges[i].w;
                    if (!vis[v])
                    {
                        vis[v]=true;
                        que.push(v);
                    }
                    if(++cont[v]>n)
                    return -1;
                }
            }
        }
        return 1;
    }  

    int main()
    {
       // freopen("cin.txt","r",stdin);
        while(~scanf("%d%d%d%lf",&N,&M,&s,&val))
        {
            int a,b;
            init();
            double rab,cab,rba,cba;
            while(M--)
            {
                scanf("%d%d%lf%lf%lf%lf",&a,&b,&rab,&cab,&rba,&cba);
                addedge(a,b,rab,cab);
                addedge(b,a,rba,cba);  

            }
            if(spfa(s,N)==-1)printf("YES\n");
            else printf("NO\n");
        }
        return 0;
    }  

10、差分约束入门题 分糖果“小于等于”  by the way
小于等于是最短路 大于等于是最长路

POJ 3159 Candies差分约束系统 spfa

核心在于:xj-xi<=bk,会发现它类似最短路中的三角不等式d[v]<=d[u]+w[u,v],即d[v]-d[u]<=w[u,v]

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int maxn=30005;
    int dis[maxn],head[maxn];
    int n,m,sum;
    bool vis[maxn];
    struct node{
        int v,w,next;
    }edge[150010];
    void addedge(int a,int b,int c){
        edge[sum].v=b;
        edge[sum].w=c;
        edge[sum].next=head[a];
        head[a]=sum++;
    }
    bool spfa(int s){
        int stack[maxn],outque[maxn],top=0;
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        memset(outque,0,sizeof(outque));
        stack[top++]=s;
        vis[s]=1;//vis数组的作用不是判断它是否被更新过 而是是否在栈里!
        dis[s]=0;
        while(top){
            int tmp=stack[--top];
            vis[tmp]=0;
            outque[tmp]++;
            if(outque[tmp]>n)  return 0;  //判断负环,当然这里没有必要写它
            int k=head[tmp];
            while(k>-1){
                 if(dis[edge[k].v]>edge[k].w+dis[tmp]){
                      dis[edge[k].v]=edge[k].w+dis[tmp];
                      if(vis[edge[k].v]==0){
                           vis[edge[k].v]=1;
                           stack[top++]=edge[k].v;
                      }
                 }
                 k=edge[k].next;
            }
        }
        return 1;
    }
    int main()
    {
        //freopen("cin.txt","r",stdin);
        while(cin>>n>>m){
            sum=0;
            memset(head,-1,sizeof(head));
            int a,b,c;
            while(m--){
                scanf("%d%d%d",&a,&b,&c);
                addedge(a,b,c);
            }
            spfa(1);
            printf("%d\n",dis[n]);
        }
        return 0;
    }  

11、差分约束系统论文题

zoj1420Cashier Employment

题意:每天每个时间有一定的收银员人数需求(以小时为单位)另有很多人应聘,一旦应聘,干满8小时,问最少雇佣多少人

s[]数组表示共雇佣了的人数,num[] 当前时刻最多可雇佣人数,r[]某时刻的最少人数。1. s[i]-s[i-1]>=0 2. s[i-1]-s[i]>=-num[i]  (1<=i<=24)  3. s[i]-s[i-8]>=r[i](8<=i<=24) 4. s[i]-s[i+16]>=r[i]-ans  (1<=i<=8)   5.  s[24]-s[0]>=ans                     
注意第4个式子,是将第3个式子扩展到"每天"得到的

#include <iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=50005;
struct Edge
{
    int v,cost;
    Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
};
vector<Edge>E[maxn];
void addedge(int u,int v,int w)
{
    E[u].push_back(Edge(v,w));
}
bool vis[maxn];
int cnt[maxn];
int dist[maxn];
bool spfa(int start,int n)
{
    memset(vis,false,sizeof(vis));
    memset(dist,-inf,sizeof(dist));
    vis[start]=true;
    dist[start]=0;
    queue<int>que;
    while(!que.empty()) que.pop();
    que.push(start);
    memset(cnt,0,sizeof(cnt));
    cnt[start]=1;
    while(!que.empty())
    {
        int u=que.front();
        que.pop();
        vis[u]=false;
        for(int i=0;i<E[u].size();i++)
        {
            int v=E[u][i].v;
            if(dist[v]<dist[u]+E[u][i].cost)
            {
                dist[v]=dist[u]+E[u][i].cost;
                if(!vis[v])
                {
                    vis[v]=true;
                    que.push(v);
                    if(++cnt[v]>n) return false;
                }
            }
        }
    }
    return true;
}
int num[30],r[30];
int main()
{
    //freopen("cin.txt","r",stdin);
    int t,ans,m;
    scanf("%d",&t);
    while(t--)
    {
        for(int i=1;i<=24;i++) scanf("%d",&r[i]);
        for(int i=0;i<25;i++) num[i]=0;
        scanf("%d",&m);
        for(int i=0;i<m;i++)
        {
            int tmp;
            scanf("%d",&tmp);
            num[tmp+1]++;///!!!
        }
        ans=1;
        bool flag=true;
        int l=0,right=m;
        while(l<right)
        {
            for(int i=0;i<=24;i++) E[i].clear();
            ans=(l+right)/2;
            for(int i=1;i<=24;i++)
            {
                addedge(i-1,i,0);
                addedge(i,i-1,-num[i]);
            }
            for(int i=8;i<=24;i++) addedge(i-8,i,r[i]);
            for(int i=1;i<=8;i++) addedge(i+16,i,r[i]-ans);
            addedge(0,24,ans);
            if(spfa(0,25)) right=ans,flag=false;
            else l=ans+1;
        }
        if(!flag) printf("%d\n",right);
        else printf("No Solution\n");
    }
    return 0;
}  

12、差分约束系统水题

hdu1531King

题意:给出asi+asi+1+…+asi+n大于或者小于ki  那么设从a[1]到a[i]的和为一个数,把它转化成最长路或者最短路都可做

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<queue>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int maxn=500;
    struct Edge
    {
        int v,cost;
        Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
    };
    vector<Edge>E[maxn];
    void addedge(int u,int v,int w)
    {
        E[u].push_back(Edge(v,w));
    }
    bool vis[maxn];
    int cnt[maxn];
    int dist[maxn];
    bool spfa(int start,int n)
    {
        memset(vis,false,sizeof(vis));
        memset(dist,-inf,sizeof(dist));
        vis[start]=true;
        dist[start]=0;
        queue<int>que;
        while(!que.empty()) que.pop();
        que.push(start);
        memset(cnt,0,sizeof(cnt));
        cnt[start]=1;
        while(!que.empty())
        {
            int u=que.front();
            que.pop();
            vis[u]=false;
            for(int i=0;i<E[u].size();i++)
            {
                int v=E[u][i].v;
                if(dist[v]<dist[u]+E[u][i].cost)
                {
                    dist[v]=dist[u]+E[u][i].cost;
                    if(!vis[v])
                    {
                        vis[v]=true;
                        que.push(v);
                        if(++cnt[v]>n) return false;
                    }
                }
            }
        }
        return true;
    }
    int main()
    {
     //   freopen("cin.txt","r",stdin);
        int n,m,si,ni,ki;
        char oi[4];
        while(~scanf("%d",&n)&&n)
        {
            scanf("%d",&m);
            for(int i=0;i<=n+2;i++) E[i].clear();
            while(m--)
            {
                scanf("%d%d%s%d",&si,&ni,oi,&ki);
                if(oi[0]=='g') addedge(si-1,si+ni,ki+1);
                else addedge(si+ni,si-1,1-ki);
            }
            for(int i=0;i<=n;i++)addedge(n+1,i,0);
            if(spfa(n+1,n+2)) puts("lamentable kingdom");
            else puts("successful conspiracy");
        }
        return 0;
    }  

13、差分约束系统较难题 :跳房子

hdu3440House Man【差分约束系统】

题意:由左至右一堆高矮不同的小房子,只能从左向右水平距离不超过d个房子,问水平最多走多远

做法:先为存有(点序号,房子高度)的结构体数组排序,枚举相邻高度的房子,为其加"d"的边,跑最短路。注意最终求的是最左边点(起点)和最右边点(终点)

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<queue>
    #include<algorithm>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int maxn=1005;
    struct Edge
    {
        int v,cost;
        Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
    };
    vector<Edge>E[maxn];
    void addedge(int u,int v,int w)
    {
        E[u].push_back(Edge(v,w));
    }
    bool vis[maxn];
    int cnt[maxn];
    int dist[maxn];
    bool spfa(int start,int n)
    {
        memset(vis,false,sizeof(vis));
        memset(dist,inf,sizeof(dist));
        vis[start]=true;
        dist[start]=0;
        queue<int>que;
        while(!que.empty()) que.pop();
        que.push(start);
        memset(cnt,0,sizeof(cnt));
        cnt[start]=1;
        while(!que.empty())
        {
            int u=que.front();
            que.pop();
            vis[u]=false;
            for(int i=0;i<E[u].size();i++)
            {
                int v=E[u][i].v;
                if(dist[v]>dist[u]+E[u][i].cost)
                {
                    dist[v]=dist[u]+E[u][i].cost;
                    if(!vis[v])
                    {
                        vis[v]=true;
                        que.push(v);
                        if(++cnt[v]>n) return false;
                    }
                }
            }
        }
        return true;
    }
    struct node
    {
        int val,pos;
    }nt[maxn];
    bool cmp(node a,node b)
    {
        return a.val<b.val;
    }
    int main()
    {
      //  freopen("cin.txt","r",stdin);
        int t,n,d,cas=1;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&d);
            for(int i=1;i<=n;i++) scanf("%d",&nt[i].val),nt[i].pos=i;
            sort(nt+1,nt+1+n,cmp);
            for(int i=0;i<=n;i++) E[i].clear();
            for(int i=1;i<n;i++)
            {
                addedge(i+1,i,-1);
                if(nt[i].pos<nt[i+1].pos)
                    addedge(nt[i].pos,nt[i+1].pos,d);
                else addedge(nt[i+1].pos,nt[i].pos,d);
            }
            printf("Case %d: ",cas++);
            int minn,maxn;
            if(nt[1].pos<nt[n].pos)minn=nt[1].pos,maxn=nt[n].pos;
            else minn=nt[n].pos,maxn=nt[1].pos;
            if(!spfa(minn,n)) printf("-1\n");
            else printf("%d\n",dist[maxn]);
        }
        return 0;
    }

14、spfa最短路+tsp状压

Hdu 4568 Hunter【spfa最短路 tsp状态压缩】

题意:n*n的矩阵,每一个格子都有相应的花费,k个宝藏的位置。从任意边界进入任意边界出去。求出得到所有宝藏的最小花费。

思路:将边界作为0点。bfs求出宝藏之间以及宝藏与0点的最短距离。一次TSP,在图中跑一次回路,从起点0回到起点,得到最小花费。

附上的也是一个13级妹子的代码,比我的那份写的好看太多

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>  

    using namespace std;
    const int INF = 0x3f3f3f3f;
    int a[205][205], d[205][205];
    int x[205], y[205], dp[1<<15][15];
    bool v[205][205];
    struct node {
        int x, y, c;
        node(int xx, int yy, int cc) {
            x = xx; y = yy; c = cc;
        }
        bool operator <(const node & A) const {
            return c > A.c;
        }
    };
    int dx[]={1, -1, 0, 0}, dy[]={0, 0, 1, -1};
    int n, m, k;  

    bool ok(int x, int y)
    {
        if(x < 0 || y < 0 || x >= n || y >= m || a[x][y] == -1) {
            return false;
        }
        return true;
    }  

    void bfs(int s, int t)
    {
        priority_queue<node> que;
        memset(v, 0, sizeof(v));
        que.push(node(x[s], y[s], 0));
        v[x[s]][y[s]] = 1;
        int c = 0;
        while(!que.empty()) {
            node e = que.top(); que.pop();
            if(e.x == x[t] && e.y == y[t]) {
                c = e.c - a[x[t]][y[t]];
            }
            for(int i = 0; i < 4; ++i) {
                int nx = e.x+dx[i], ny = e.y+dy[i];
                if(v[nx][ny] || a[nx][ny] == -1)
                    continue;
                if(nx < 0 || ny < 0 || nx >= n || ny >= m) {  

                    d[s][0] = min(d[s][0], e.c);
                    d[0][s] = min(d[0][s], e.c);
                    continue;
                }  

                v[nx][ny] = 1;
                que.push(node(nx, ny, e.c+a[nx][ny]));
            }
        }
        if(s != t) {
            d[s][t] = c;
            d[t][s] = c;
        }
    }
    void deal_p_p()
    {
        memset(d, 0x3f, sizeof(d));
        for(int i = 1; i <= k; ++i) {
            for(int j = 1; j <= k; ++j) {
                //while(!que.empty()) que.pop();
                bfs(i, j);
            }
        }
    }  

    int solve()
    {
        memset(dp, 0x3f, sizeof(dp));
        int nk = k+1;
        dp[(1<<nk)-1][0] = 0;
        for(int s = (1<<nk)-2; s >= 0; --s) {
            for(int v = 0; v < nk; ++v) {
                for(int u = 0; u < nk; ++u) {
                    if(!(s>>u &1)) {
                        dp[s][v] = min(dp[s][v], dp[s|1<<u][u]+d[v][u]);
                    }
                }
            }
        }
        return dp[0][0];
    }  

    int main()
    {
        //freopen("in", "r", stdin);
        int T;
        scanf("%d", &T);
        while(T--) {
            scanf("%d%d", &n, &m);
            memset(a, 0, sizeof(a));
            for(int i = 0; i < n; ++i) {
                for(int j = 0; j < m; ++j) {
                    scanf("%d", &a[i][j]);
                }
            }
            int Sum = 0;
            scanf("%d", &k);
            memset(x, 0, sizeof(x));
            memset(y, 0, sizeof(y));
            for(int i = 1; i <= k; ++i) {
                scanf("%d%d", &x[i], &y[i]);
                Sum += a[x[i]][y[i]];
            }
            deal_p_p();
            if(k == 1) {
                if(d[1][0] == INF) {
                    printf("0\n");
                }
                else printf("%d\n", 2*d[1][0]+a[x[1]][y[1]]);
            }
            else {
                int ans = solve();
                if(ans == INF) printf("0\n");
                else printf("%d\n", ans+Sum);
            }
        }
        return 0;
    }

15、取消一点使得最短路有最大值

hdu5137How Many Maos Does the Guanxi Worth

题意:暴发户给孩子办关系升学,需要一个人找一个人,人情钱都是这货出,你要劝说其中一个人不去帮忙使得暴发户花钱最多

枚举所有点,逐次跑最短路即可

#include <iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn=33;
    const int inf=0x3f3f3f3f;
    bool vis[maxn];
    int pre[maxn],lowcost[maxn],cost[maxn][maxn];
    void dijkstra(int n,int beg)
    {
        for(int i=0;i<n;i++)
        {
            lowcost[i]=inf;vis[i]=false;pre[i]=-1;
        }
        lowcost[beg]=0;
        for(int j=0;j<n;j++)
        {
            int k=-1;
            int Min=inf;
            for(int i=0;i<n;i++)
                if(!vis[i]&&lowcost[i]<Min)
                {
                    Min=lowcost[i];
                    k=i;
                }
            if(k==-1)break;
            vis[k]=true;
            for(int i=0;i<n;i++)
                if(!vis[i]&&lowcost[k]+cost[k][i]<lowcost[i])
                {
                    lowcost[i]=lowcost[k]+cost[k][i];
                    pre[i]=k;
                }
        }
    }
    int temp[maxn];
    int main()
    {
       // freopen("cin.txt","r",stdin);
        int n,m;
        while(~scanf("%d%d",&n,&m)&&n&&m)
        {
            memset(cost,inf,sizeof(cost));
            for(int i=0;i<m;i++)
            {
                int a,b,c;
                scanf("%d%d%d",&a,&b,&c);
                a--;b--;
                if(c>cost[a][b]) continue;
                cost[a][b]=cost[b][a]=c;
            }
            int ans=0;  

            for(int i=1;i<n-1;i++)
            {
                for(int j=0;j<=n-1;j++)
                {
                    temp[j]=cost[i][j];
                    cost[i][j]=cost[j][i]=inf;
                }
                dijkstra(n,0);
                if(lowcost[n-1]>ans) ans=lowcost[n-1];
                for(int j=0;j<n;j++)
                    cost[i][j]=cost[j][i]=temp[j];
            }
            if(ans<inf) printf("%d\n",ans);
            else printf("Inf\n");
        }
        return 0;
    }

16、最短路反向建图(酋长是终点不是起点,最开始所有点前面再加一个源点)枚举等级区间依次跑最短路即可

poj1062昂贵的聘礼

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn=133;
    const int inf=0x3f3f3f3f;
    bool vis[maxn];
    int pre[maxn],lowcost[maxn],cost[maxn][maxn],val[maxn],rank[maxn];
    void dijkstra(int cost[][maxn],int n,int beg)
    {
        for(int i=0;i<n;i++)
        {
            lowcost[i]=inf;vis[i]=false;pre[i]=-1;
        }
        lowcost[beg]=0;
        for(int j=0;j<n;j++)
        {
            int k=-1;
            int Min=inf;
            for(int i=0;i<n;i++)
                if(!vis[i]&&lowcost[i]<Min)
                {
                    Min=lowcost[i];
                    k=i;
                }
            if(k==-1)break;
            vis[k]=true;
            for(int i=0;i<n;i++)
                if(!vis[i]&&lowcost[k]+cost[k][i]<lowcost[i])
                {
                    lowcost[i]=lowcost[k]+cost[k][i];
                    pre[i]=k;
                }
        }
    }
    int tmp[maxn][maxn];
    int main()
    {
        freopen("cin.txt","r",stdin);
        int m,n;
        while(~scanf("%d%d",&m,&n))
        {
            memset(cost,inf,sizeof(cost));
            int maxn=0,minn=inf;
            for(int i=0;i<n;i++)
            {
                int x;
                scanf("%d%d%d",&cost[n][i],&rank[i],&x);
                if(rank[i]>maxn)maxn=rank[i];
                if(rank[i]<minn)minn=rank[i];
                while(x--)
                {
                    int t,v;
                    scanf("%d%d",&t,&v);t--;
                    if(v<cost[i][t]) cost[i][t]=v;
                }
            }
            int ans=inf;
            for(int k=max(0,rank[0]-m);k<=rank[0]+m;k++)
            {
                for(int i=0;i<n;i++)
                    for(int j=0;j<n;j++)
                    {
                        if(rank[i]>=k&&rank[i]<=k+m&&rank[j]>=k&&rank[j]<=k+m)
                            tmp[i][j]=cost[i][j];
                        else tmp[i][j]=inf;
                    }
                dijkstra(tmp,n,0);
                for(int i=0;i<n;i++)
                    if(ans>lowcost[i]+cost[n][i])
                        ans=lowcost[i]+cost[n][i];
            }
            printf("%d\n",ans);
        }
        return 0;
    }  

17、送餐来回的最短距离

看到这个题的时候想到上面的第7题了,然而第7题不需要每个点都遍历一遍,而这个题需要,所以这个是需要用状压的tsp的

poj3311 Hie with the Pie【floyd最短路+状态压缩】

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int dp[1<<11][20],n,num[11][11];
    int min(int a,int b){if(a<b)return a;return b;}
    int judge(int s)
    {
        int num=0;
        while(s)
        {
            if(s&1)num++;
            s>>=1;
        }
        return num;
    }
    int dist[11][11];
    void init()
    {
        for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        {
            dist[i][j]=num[i][j];
        }
        for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        for(int k=0;k<n;k++)
        if(dist[j][i]+dist[i][k]<dist[j][k])
        dist[j][k]=dist[j][i]+dist[i][k];
        for(int i=0;i<n;i++)
        for(int j=0;j<n;j++);
      //  printf("i=%d,j=%d,dist=%d\n",i,j,dist[i][j]);
    }
    int main()
    {
      //  freopen("cin.txt","r",stdin);
        while(~scanf("%d",&n)&&n)
        {
            for(int i=0;i<=n;i++)
                for(int j=0;j<=n;j++)
                    scanf("%d",&num[i][j]);
            memset(dp,0x3f3f3f3f,sizeof(dp));
            dp[0][0]=0;
            n++;//have n+1 points
            init();
            for(int i=0;i<n;i++)//n points
                dp[1<<i|1][i]=dist[0][i];
           // for(int i=0;i<(1<<n)-1;i++) if(dp[i]!=0x3f3f3f3f)printf("i=%d  dp=%d   ",i,dp[i]);
            int ans=0x3f3f3f3f;
            for(int i=1;i<(1<<n)-1;i++)//now
            {
                for(int j=0;j<n;j++)//not exist
                {
                    if(i&(1<<j)) continue;
                    for(int k=0;k<n;k++)//exist
                    {
                        if(j==k) continue;
                        if(i&(1<<k))
                        {
                            dp[1<<j|i][j]=min(dp[i][k]+dist[k][j],dp[1<<j|i][j]);
                            if(judge(1<<j|i)==n)
                                ans=min(ans,dp[1<<j|i][j]+dist[j][0]);
                        }
                    }
                }
            }
            printf("%d\n",ans);
        }
        return 0;
    }  

18、建全国的路的一部分,依旧要求联通,但是首都到各个城市的最短距离不变的前提下求最小花费

Aizu 2249Road Construction 单源最短路变形 spfa模板改写

既然是首都到各地的距离最短前提下求最小花费,那么松弛操作当距离相等的时候压入花费小的就可以啦

    #include <stdio.h>
    #include <string.h>
    #include <queue>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int maxn=10050;
    const int maxe=2005000;
    const int INF=1e9;
    struct note
    {
        int to;
        int w;
        int c;
        int next;
    };
    note edge[maxe];
    int head[maxn];
    int ip;
    void init()
    {
        memset(head,-1,sizeof(head));
        ip=0;
    }
    void addedge(int u,int v,int w,int c)
    {
        edge[ip].to=v;
        edge[ip].c=c;
        edge[ip].w=w;
        edge[ip].next=head[u];
        head[u]=ip++;
    }
    int cost[maxn];
    int dis[maxn];
    bool vis[maxn];
    queue<int>q;
    void spfa(int s,int n)
    {
        while(!q.empty())q.pop();
        for(int i=1;i<=n;i++)
            cost[i]=dis[i]=INF;
        memset(vis,false,sizeof(vis));
        dis[s]=0;
        cost[s]=0;
        vis[s]=true;
        q.push(s);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            vis[u]=false;
            for(int i=head[u];i!=-1;i=edge[i].next)
            {
                int to=edge[i].to;
                int val=edge[i].w;
                if(dis[to]>dis[u]+val||(dis[to]==dis[u]+val&&cost[to]>edge[i].c))//有多条最短路时,取花费最小的
                {
                    dis[to]=dis[u]+val;
                    cost[to]=edge[i].c;
                    if(!vis[to])
                    {
                        q.push(to);
                        vis[to]=true;
                    }
                }
            }
        }
    }
    int main()
    {
       // freopen("cin.txt","r",stdin);
        int n,m;
        int u,v,w,c;
        while(~scanf("%d%d",&n,&m))
        {
            if(n==0&&m==0) break;
            init();
            for(int i=0;i<m;i++)
            {
                scanf("%d%d%d%d",&u,&v,&c,&w);
                addedge(u,v,c,w);
                addedge(v,u,c,w);
            }
            spfa(1,n);
            int ans=0;
            for(int i=1;i<=n;i++)
                ans+=cost[i];
            printf("%d\n",ans);
        }
        return 0;
    }  

19、差分约束系统

uva11478 Halum【二分+差分约束】

题意:对于给定有向图,定义一种操作:对于选定的某一点,进入这个点的所有边权都减去d,从这个点出去的所有边权都加上d。经过一系列的操作,使得所有边权的最小值达到最大,需保证这个最值是正数

把点当做最短路的点假设每个点都有一个点权sum(a),而w(a,b)+sum(a)-sum(b)>=x,x是所有边权的最小值,把边权sum放到等号右边,就构成了差分约束的不等式组,,保证不出现负环的spfa就是最大的x求得的。

by the way Spfa要是最开始不加超级源点,就要把所有的点都压入队列

这么看来就和上面的题一样啦

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<queue>
    using namespace std;
    const int inf=0x3f3f;
    const int maxn=505;
    int n,m;
    struct Edge
    {
        int v,cost;
        Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
    };
    vector<Edge>E[maxn];
    void addedge(int u,int v,int w)
    {
        E[u].push_back(Edge(v,w));
    }
    bool vis[maxn];
    int cnt[maxn];
    int dist[maxn];
    bool spfa(int start,int n)
    {
        memset(vis,false,sizeof(vis));
        memset(dist,inf,sizeof(dist));
        vis[start]=true;
        dist[start]=0;
        queue<int>que;
        while(!que.empty()) que.pop();
        que.push(start);
        memset(cnt,0,sizeof(cnt));
        cnt[start]=1;
        while(!que.empty())
        {
            int u=que.front();
            que.pop();
            vis[u]=false;
            for(int i=0;i<E[u].size();i++)
            {
                int v=E[u][i].v;
                if(dist[v]>dist[u]+E[u][i].cost)
                {
                    dist[v]=dist[u]+E[u][i].cost;
                    if(!vis[v])
                    {
                        vis[v]=true;
                        que.push(v);
                        if(++cnt[v]>n) return false;
                    }
                }
            }
        }
        return true;
    }
    bool judge(int x)
    {
       // cout<<"rrr"<<endl;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<E[i].size();j++)
                E[i][j].cost-=x;
        }
        bool flag=spfa(0,n);
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<E[i].size();j++)
                E[i][j].cost+=x;
        }
        return flag;
    }
        int main()
        {
            //freopen("cin.txt","r",stdin);
           // int cas=1;
           // scanf("%d",&t);
            while(~scanf("%d%d",&n,&m))
            {
               // printf("Case #%d: ",cas++);
                for(int i=0;i<=n;i++) E[i].clear();
            //    memset(head,-1,sizeof(head));
                int l=1,mid,r=-inf;
                while(m--)
                {
                    int a,b;
                    int c;
                    scanf("%d%d%d",&a,&b,&c);
                    addedge(a,b,c);
                    if(c>r)r=c;
                }
                for(int i=1;i<=n;i++)addedge(0,i,0);
              //  cout<<"rrr"<<endl;
               // printf("r=%d\n",r);
                if(judge(r))
                {
                    printf("Infinite\n");
                    continue;
                }
                else if(!judge(1))
                {
                    puts("No Solution");
                    continue;
                }
                int ans;
                while(l<=r)
                {
                    mid=(l+r)/2;
                   // cout<<mid<<endl;
                   // printf("l=%d,mid=%d,r=%d    ",l,mid,r);
                    if(judge(mid)) ans=mid,l=mid+1;
                    else {r=mid-1;}
                   // printf("l=%d,mid=%d,r=%d,ans=%d\n",l,mid,r,ans);
                }
                printf("%d\n",ans);
            }
            return 0;
        }

20、二分找负环

UVA - 11090 Going in Cycle!! 【Bellman-Ford算法判负环 二分】

一样的题:给定有向带权图,求最小环的值。二分找最小值,边权都减去这个数

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    using namespace std;
    const double inf=0x3f3f3f3f;
    const int maxn=100;
    int n,m,t;
    double dist[maxn];
    struct Edge
    {
        int u,v;
        double cost;
        Edge(int _u=0,int _v=0,double _cost=0):u(_u),v(_v),cost(_cost){}
    };
    vector<Edge>E;
    bool bellman(int start,int n)
    {
        for(int i=1;i<=n;i++)dist[i]=inf;
        dist[start]=0;
        for(int i=1;i<n;i++)
        {
            bool flag=false;
            for(int j=0;j<E.size();j++)
            {
                int u=E[j].u;
                int v=E[j].v;
                double cost=E[j].cost;
                if(dist[v]>dist[u]+cost)
                {
                    dist[v]=dist[u]+cost;
                    flag=true;
                }
            }
            if(!flag)return true;
        }
        for(int j=0;j<E.size();j++)
            if(dist[E[j].v]>dist[E[j].u]+E[j].cost)
                return false;
        return true;
    }
    bool judge(double x)
    {
        for(int i=0;i<E.size();i++) E[i].cost-=x;
        bool flag=bellman(1,n);
        for(int i=0;i<E.size();i++) E[i].cost+=x;
        return flag;
    }
    int main()
    {
        //freopen("cin.txt","r",stdin);
        int cas=1;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
            printf("Case #%d: ",cas++);
            E.clear();
            double r=0.0;
            while(m--)
            {
                int a,b;
                double c;
                scanf("%d%d%lf",&a,&b,&c);
                E.push_back(Edge(a,b,c));
                if(c>r)r=c;
            }
          //  printf("r=%lf\n",r);
            if(judge(r+1))
            {
                printf("No cycle found.\n");
                continue;
            }
            double l=0.0,mid;
            while(r-l>=0.0001)
            {
                mid=(l+r)/2;
                for(int i=0;i<E.size();i++) E[i].cost-=mid;
                if(bellman(1,n)) l=mid;
                else r=mid;
                for(int i=0;i<E.size();i++) E[i].cost+=mid;
            }
            printf("%.2lf\n",r);
        }
        return 0;
    }

21、

hdu4786Fibonacci Tree【kruskal最小生成树】

给定无向图每个边要么是白色,要么是黑色。求生成树的白边个数能否是斐波那契数。其实应该能想到的,让白边是1,黑边是0,求最大生成树和最小生成树。看中间范围是否有斐波那契数。

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=200005;
    const int maxm=200005;
    int F[maxn];
    struct Edge
    {
        int u,v,w;
    }edge[maxm];
    int tol;
    void addedge(int u,int v,int w)
    {
        edge[tol].v=v;
        edge[tol].u=u;
        edge[tol++].w=w;
    }
    bool cmp(Edge a,Edge b)
    {
        return a.w<b.w;
    }
    bool cmp2(Edge a,Edge b)
    {
        return a.w>b.w;
    }
    int fnd(int x)
    {
        return x==F[x]?x:F[x]=fnd(F[x]);
    }
    int kruskal(int n)
    {
        for(int i=1;i<=n;i++) F[i]=i;
       // sort(edge,edge+tol,cmp);
        int cnt=0;
        int ans=0;
        for(int i=0;i<tol;i++)
        {
            int u=edge[i].u;
            int v=edge[i].v;
            int w=edge[i].w;
            int a=fnd(u);
            int b=fnd(v);
            if(a!=b)
            {
                ans+=w;
                F[b]=a;
                cnt++;
            }
            if(cnt==n-1)break;
        }
        if(cnt<n-1)return -1;
        return ans;
    }
    int num[100],cnt;
    void init()
    {
        num[0]=0;num[1]=1;num[2]=2;num[3]=3;
        cnt=4;
        while(num[cnt-1]<=100005)
        {
            num[cnt]=num[cnt-1]+num[cnt-2];
            cnt++;
           // cout<<num[cnt-1]<<endl;
        }
    }
    int main()
    {
        //freopen("cin.txt","r",stdin);
        int t,n,m,cas=1;
        scanf("%d",&t);
        init();
        while(t--)
        {
            scanf("%d%d",&n,&m);
            tol=0;
            while(m--)
            {
                int u,v,w;
                scanf("%d%d%d",&u,&v,&w);
                addedge(u,v,w);
                addedge(v,u,w);///!!!
            }
            printf("Case #%d: ",cas++);
            sort(edge,edge+tol,cmp);
            int small=kruskal(n);
            bool ff=true;
            sort(edge,edge+tol,cmp2);
            int big=kruskal(n);
           // printf("s=%d,b=%d\n",small,big);
            if(small==-1||big==-1)
            {
                puts("No");
                continue;
            }
            bool flag=false;
            for(int i=1;i<=23;i++)
            {
                if(num[i]>=small&&num[i]<=big)
                {
                    flag=true;
                    break;
                }
            }
            if(flag) printf("Yes\n");
            else printf("No\n");
        }
        return 0;
    }  

22、2015多校第一场(盗墓笔记)

hdu5294Tricks Device【最短路+网络流】

从1开始到n结束,给出双向道路,问至少去掉多少边最短路不是原始最短路长度,至多去掉多少条边最短路长度不变

分别利用1与n作为源点 ,分别跑一遍最短路,这个时候两个dist数组记录的就是以1、n为源点的单源最短路长度,理论上他俩无缝接起来都是最短路的方案(理论上是,实际上也是:P  )那么我就依次验证给定的边长是否在最短路上,在的话,给这对点建网络流的边(这个题最最讨厌的地方在于函数名,数组名都差不多,特别容易写错)流量是题中给定的第三个数组,求完最短路还要给这对点再建一遍最短路的边,边长为一,这样跑下来dist[n]就是最短路的最短长度了

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<queue>
    using namespace std;
    const int inf=0x3f3f;
    const int maxn=2111;
    int n,m;
    struct Edge
    {
        int v,cost;
        Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
    };
    vector<Edge>E[maxn];
    void addedge(int u,int v,int w)
    {
        E[u].push_back(Edge(v,w));
    }
    bool vis[maxn];
    int cnt[maxn];
    int dist[maxn],dist1[maxn];
    bool spfa(int start,int n)
    {
        memset(vis,false,sizeof(vis));
        memset(dist,inf,sizeof(dist));
        vis[start]=true;
        dist[start]=0;
        queue<int>que;
        while(!que.empty()) que.pop();
        que.push(start);
        memset(cnt,0,sizeof(cnt));
        cnt[start]=1;
        while(!que.empty())
        {
            int u=que.front();
            que.pop();
            vis[u]=false;
            for(int i=0;i<E[u].size();i++)
            {
                int v=E[u][i].v;
                if(dist[v]>dist[u]+E[u][i].cost)
                {
                    dist[v]=dist[u]+E[u][i].cost;
                    if(!vis[v])
                    {
                        vis[v]=true;
                        que.push(v);
                        if(++cnt[v]>n) return false;
                    }
                }
            }
        }
        return true;
    }
    int w[60009],u[60009],v[60009];
    ////////////////////////////////////////
    const int mm=161111;
    const int mn=600009;
    const int oo=1000000000;
    int node,src,dest,edge;
    int reach[mm],flow[mm],nxt[mm];
    int head[mn],work[mn],dis[mn],q[mn];
    inline int min(int a,int b)
    {
        return a<b?a:b;
    }
    inline void prepare(int _node,int _src,int _dest)
    {
        node=_node,src=_src,dest=_dest;
        for(int i=0;i<node;++i)head[i]=-1;
        edge=0;
    }
    inline void addedge(int u,int v,int c1,int c2)
    {
        reach[edge]=v,flow[edge]=c1,nxt[edge]=head[u],head[u]=edge++;
        reach[edge]=u,flow[edge]=c2,nxt[edge]=head[v],head[v]=edge++;
    }
    bool Dinic_bfs()
    {
        int i,u,v,l,r=0;
        for(i=0;i<node;++i)dis[i]=-1;
        dis[q[r++]=src]=0;
        for(l=0;l<r;++l)
            for(i=head[u=q[l]];i>=0;i=nxt[i])
                if(flow[i]&&dis[v=reach[i]]<0)
                {
                    dis[q[r++]=v]=dis[u]+1;
                    if(v==dest)return 1;
                }
        return 0;
    }
    int Dinic_dfs(int u,int exp)
    {
        if(u==dest)return exp;
        for(int &i=work[u],v,tmp;i>=0;i=nxt[i])
            if(flow[i]&&dis[v=reach[i]]==dis[u]+1&&(tmp=Dinic_dfs(v,min(exp,flow[i])))>0)
            {
                flow[i]-=tmp;
                flow[i^1]+=tmp;
                return tmp;
            }dis[u]--;
        return 0;
    }
    int Dinic_flow()
    {
        int i,ret=0,delta;
        while(Dinic_bfs())
        {
            for(i=0;i<node;++i)work[i]=head[i];
            while(delta=Dinic_dfs(src,oo))ret+=delta;
        }
        return ret;
    }
    int bb[mn][2],dist2[mn];
    int main()
    {
       // freopen("cin.txt","r",stdin);
        while(~scanf("%d%d",&n,&m))
        {
            for(int i=0;i<=n;i++) E[i].clear();
            for(int i=0;i<m;i++)
            {
                scanf("%d%d%d",&u[i],&v[i],&w[i]);
                addedge(u[i],v[i],w[i]);
                addedge(v[i],u[i],w[i]);
            }
            spfa(1,n);
            memcpy(dist2,dist,sizeof(dist));
            spfa(n,n);
            int k=0;
            for(int i=0;i<m;i++)
            {
                if(dist2[u[i]]>dist2[v[i]])swap(u[i],v[i]);
                if(dist[v[i]]+w[i]+dist2[u[i]]==dist2[n])
                {
                    bb[k][0]=u[i];
                    bb[k++][1]=v[i];
                }
            }
            prepare(n+1,1,n);
            for(int i=0;i<k;i++)
            {
                addedge(bb[i][0],bb[i][1],1,0);
               // addedge(bb[i][1],bb[i][0],1);
            }
            int x=Dinic_flow();
            for(int i=0;i<=n;i++) E[i].clear();
            for(int i=0;i<k;i++)
            {
                addedge(bb[i][0],bb[i][1],1);
                addedge(bb[i][1],bb[i][0],1);
            }
            spfa(1,n);
            printf("%d %d\n",x,m-dist[n]);
        }
        return 0;
    }

23、次小生成树(秦始皇)

hdu4081Qin Shi Huang‘s National Road System【次小生成树】

说题意:秦始皇统一中国之后要在全国修公路连接各个城市,抠门皇帝只想修成最小生成树(距离最小,不考虑人力),一个道士说自己可以不花人力物力修一条路,经过两方妥协,选择max(两个城市人口/生成树长度-这条路的长度)的路让他变,求这个比值最大值。

这个题没问次小生成树的值,但是用到了路径最长边这个数组,我叫他Max[][],题中问的是(两个城市人口/生成树长度-这条路的长度),我们首先考虑分母最小,因为对于某个边来说分子是定值啊,分母分成两种情况:1.这个边是最小生成树上的,那啥也别说,mst-cost[][]即可 2.这条边不在最小生成树上:那就是含有这个边的某个生成树,(这么说来这个题应该归纳到次小生成树啊==),mst-Max+cost即可

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    const int maxn=1010;
    const int inf=0x3f3f3f3f;
    bool vis[maxn],used[maxn][maxn];
    double lowc[maxn];
    int pre[maxn];
    double Max[maxn][maxn],cost[maxn][maxn];
    struct node
    {
        int x,y,pop;
    }num[1010];
    double min(double a,double b){if(a<b) return a;return b;}
    double max(double a,double b){if(a>b) return a;return b;}
    inline double Dist(node v1,node v2)
    {
        return sqrt(double(v1.x-v2.x)*(v1.x-v2.x)+double(v1.y-v2.y)*(v1.y-v2.y));
    }
    double prim(int n)
    {
        double ans=0;
        memset(vis,false,sizeof(vis));
        memset(Max,0,sizeof(Max));
        memset(used,false,sizeof(used));
        vis[0]=true;
        pre[0]=-1;
        for(int i=0;i<n;i++)
        {
            lowc[i]=cost[0][i];
            pre[i]=0;
        }
      //  lowc[0]=0.0;
        for(int i=1;i<n;i++)
        {
            double minc=1.0*inf;
            int p=-1;
            for(int j=0;j<n;j++)
                if(!vis[j]&&(minc>lowc[j]||p==-1))
                {
                    minc=lowc[j];
                    p=j;
                }
            ans+=minc;
            vis[p]=true;
            used[p][pre[p]]=used[pre[p]][p]=true;
            for(int j=0;j<n;j++)
            {
                if(vis[j]&&j!=p)//j!=p必须加!!1
                    Max[j][p]=Max[p][j]=max(Max[j][pre[p]],lowc[p]);
                if(!vis[j]&&lowc[j]>cost[p][j])
                {
                    lowc[j]=cost[p][j];
                    pre[j]=p;
                }
            }
        }
        return ans;
    }
    int main()
    {
        //freopen("cin.txt","r",stdin);
        int t,n;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d",&n);
            for(int i=0;i<n;i++) scanf("%d%d%d",&num[i].x,&num[i].y,&num[i].pop);
            for(int i=0;i<n;i++)
                for(int j=i+1;j<n;j++)
                    cost[i][j]=cost[j][i]=Dist(num[i],num[j]);
            double mst=prim(n);
           // double mst=smst(n,ans);
            double ans=-1;
            for(int i=0;i<n;i++)
                for(int j=i+1;j<n;j++)
                {
                    if(used[i][j])//on mst
                        ans=max(ans,1.0*(num[i].pop+num[j].pop)/(mst-cost[i][j]));
                    else
                        ans=max(ans,1.0*(num[i].pop+num[j].pop)/(mst-Max[i][j]));
                }
            printf("%.2f\n",ans);
        }
        return 0;
    }
时间: 2024-08-29 05:35:35

生成树&最短路总结篇的相关文章

POJ 1797 Heavy Transportation(最大生成树/最短路变形)

传送门 Heavy Transportation Time Limit: 3000MS   Memory Limit: 30000K Total Submissions: 31882   Accepted: 8445 Description Background Hugo Heavy is happy. After the breakdown of the Cargolifter project he can now expand business. But he needs a clever

诗经 全文

诗经 全文 (带注释和译文) http://www.edu009.com/Article/HTML/Article_60756.html <诗经> 春秋·孔丘 <诗经>是我国第一部诗歌总集,先秦时代称为“诗”或“诗三百”,孔子加以了整理.汉武帝采纳董仲舒“罢黜百家,独尊儒术”的建议,尊“诗”为经典,定名为<诗经>. <诗经>现存诗歌 305 篇,包括西周初年到春秋中叶共 500 余年的民歌和朝庙乐章,分为风.雅.颂三章. “风”包括周南.召南.邶.鄘.卫.王

51nod 1443 路径和树——最短路生成树

题目:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1443 不只是做一遍最短路.还要在可以选的边里选最短的才行. 以为是求一遍最短路,然后按边权对边排序,哪条边两边的dis正好吻合,就把该边的边权加到ans里,把两端加到并查集里. 但其实不对.因为忽略了方向.比如如果有多个点同样地可以更新一个点,算的时候可能这多个点都因为那个点而被合到了并查集里,但其实只能有一个被合进去. 其实只要松弛点的时候如果dis[v]==d

bzoj 4283 魔法少女伊莉雅 - 最短路

题目传送门 需要高级权限的快速通道 题目大意 给定一个$n$个点$m$条边的带正权无向图.要求找一条路径满足: 它是一条简单路径 它是一条严格次短路 对于任何一条可能存在于最短路上的边,不能包含它的反向边. 不存在这条路径输出-1. 神题orz...orz....orz....良心的最短路性质题,涵盖了大部分最短路径树和最短路径图上的常用性质. 直觉是最短路上随意判一判就好了.然后全WA了,qwq.. 然后开始讲正题. 将$d\left(u, v \right )$记为点$u$到点$v$的最短路

[转]01分数规划算法 ACM 二分 Dinkelbach 最优比率生成树 最优比率环

01分数规划 前置技能 二分思想最短路算法一些数学脑细胞?问题模型1 基本01分数规划问题 给定nn个二元组(valuei,costi)(valuei,costi),valueivaluei是选择此二元组获得的价值(非负),costicosti是选择此二元组付出的代价(非负),设xi(xi∈{0,1})xi(xi∈{0,1})代表第ii个二元组的选与不选,最大(小)化下式 maximize(or minimize)   r=∑valuei?xi∑costi?ximaximize(or minim

【66测试20161115】【树】【DP_LIS】【SPFA】【同余最短路】【递推】【矩阵快速幂】

还有3天,今天考试又崩了.状态还没有调整过来... 第一题:小L的二叉树 勤奋又善于思考的小L接触了信息学竞赛,开始的学习十分顺利.但是,小L对数据结构的掌握实在十分渣渣.所以,小L当时卡在了二叉树. 在计算机科学中,二叉树是每个结点最多有两个子结点的有序树.通常子结点被称作“左孩子”和“右孩子”.二叉树被用作二叉搜索树和二叉堆.随后他又和他人讨论起了二叉搜索树.什么是二叉搜索树呢?二叉搜索树首先是一棵二叉树.设key[p]表示结点p上的数值.对于其中的每个结点p,若其存在左孩子lch,则key

NOIP2013 货车运输(最大生成树+LCA)

模拟考试的时候暴搜,结果写丑了,分都不分 下来啃了一下题解,发现要用到一个叫做倍增的东西,还没有学过.但是老师说的,没有那个东西,写暴力也有30~40分... 我觉得最大生成树还是很好理解的,因为我们要求的是图中任意两个点之间的路径上,使得边权的最小值尽量大.因此首先求最大生成树. 当我们得到最大生成树后,要求两个点之间边权最小值,我们可以首先找到他们的公共祖先.这里有一篇写得很详细的代码,并且注明了各种写法的得分http://blog.csdn.net/gengmingrui/article/

POJ1679 The Unique MST 生成树+DFS

题意:求一个无向图的最小生成树与次小生成树的边权和是否相等 题解: 首先有一个性质,就是最小生成树上的任意两点的距离就是其在原图中的最短路,严格的证明我不会- -,但是由于Prim Dijkstra算法的过程完全相同,所以这个性质比较显然(不会证就不会证呗为何还要找理由QAQ) 还要一条性质就是无向MST上加入一条边一定会形成一个环,因此我们可以枚举加入一条不在MST上的边e,然后删除MST上形成的环中除e之外的最长的一条边,得到了一个新的最小生成树,由于e一定大于等于删除的那条边,因此得到的新

Javascript基础篇小结

Javascript基础篇小结 字数9973 阅读3975 评论7 喜欢28 转载请声明出处 博客原文 随手翻阅以前的学习笔记,顺便整理一下放在这里,方便自己复习,也希望你有也有帮助吧 第一课时 入门基础 知识点: 操作系统就是个应用程序 只要是应用程序都要占用物理内存 浏览器本身也是一个应用程序 浏览器本身只懂得解析HTML 调用浏览器这个应用程序的一个功能绘制 1.javascript介绍 JavaScript操作DOM的本质是=获取+触发+改变 目的:就是用来操作内存中的DOM节点 修改D