生成树&最短路总结篇

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;  
    }  





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值