PKU 3422 &&PKU 2135 &&ZJU 3308

三题属于同一类型的题,即最小费用流问题。。

PKU http://acm.pku.edu.cn/JudgeOnline/problem?id=3422

题意:给定一个矩阵,从[1][1]—>[n][n]走k次所能得到的最大值;

建图:把一个点Q,val[i][j]拆成两个点,Q`,它们之间连线,流量为1,费用为val[i][j];

        Q`与右边和下边的两个点连线构图,然后增加一个超级源点s,超级汇点t,连s->0,Q`(val[i][j])—>t,流量为k,费用为0;

也可以视为控制增光次数的费用流。。。

代码:

#include<iostream>
#include<queue>
using namespace std ;
const int N = 51 , M = 200001 , Inf = 999999999 ;
int  n,k,en,s,t,val[N][N],indx[M],road[M],pre[M];
struct Node
{
     int vdx,flow,cost,next;   
}Edge[M]; 
void add(int from,int to,int cost,int flow)
{
     Edge[en].vdx = to ;
     Edge[en].flow= flow ;
     Edge[en].cost= cost ;
     Edge[en].next= indx[from] ;
     indx[from] = en ;
     en++ ; 
     Edge[en].vdx = from ;
     Edge[en].flow= 0 ;
     Edge[en].cost= - cost ;
     Edge[en].next= indx[to] ;
     indx[to] = en ;
     en++ ; 
}
bool spfa()
{
     int visit[M],d[M];
     queue<int>q; 
     memset(visit,0,sizeof(visit));
     memset(d,-1,sizeof(d));
     visit[s]=1,d[s]=0;
     q.push(s);
     while(!q.empty())
     {
          int cur=q.front();
          q.pop();
          visit[cur]=0;
          int next=indx[cur];
          while(next!=-1)
          {
              if(Edge[next].flow>0&&d[Edge[next].vdx]<d[cur]+Edge[next].cost)
              {
                   d[Edge[next].vdx] = d[cur]+Edge[next].cost ; 
                   pre[Edge[next].vdx] = cur ;
                   road[Edge[next].vdx]= next;
                   if(0==visit[Edge[next].vdx])
                   {
                       visit[Edge[next].vdx] = 1 ;
                       q.push(Edge[next].vdx);   
                   }                                                             
              }  
              next=Edge[next].next;
          } 
           
     }
    if(d[t]==-1) return false  ;
     return true  ; 
}    
int maxflow; 
void argue()
{
     int minflow=Inf;
     for(int v=t;v!=s;v=pre[v])
         minflow = min(minflow,Edge[road[v]].flow);
     for(int v=t;v!=s;v=pre[v])
     {
         maxflow += minflow*Edge[road[v]].cost;
         Edge[road[v]].flow -= minflow ;
         Edge[road[v]^1].flow +=minflow ;   
     }     
} 
int main()
{
      scanf("%d%d",&n,&k);
      for(int i=0;i<n;i++)
       for(int j=0;j<n;j++)
        scanf("%d",&val[i][j]);       
      en=0;
      s=2*n*n+1,t=s+1 ;
      int num=n*n+1;  
      memset(indx,-1,sizeof(indx)); 
      for(int i=0;i<n;i++)
       for(int j=0;j<n;j++)
       {
           int x=i*n+j;
           add(x,x+num,val[i][j],1);
           add(x,x+num,0,Inf);
           if(i+1<n) 
            add(x+num,x+n,0,Inf);
           if(j+1<n) 
            add(x+num,x+1,0,Inf);
          
       }             
       add(s,0,0,k);
       add(n*n-1+num,t,0,k);
       maxflow=0;
       while(spfa()) argue();               
      printf("%d/n",maxflow);
   //  system("pause");
     return 0 ;
}
 

PKU http://acm.pku.edu.cn/JudgeOnline/problem?id=2135

题意:给定一个无向图,设1为起点,n为终点,求从1到n然后从n返回1的一个最小环,并且每次不能走重复边;

很显然这是球一个最小环,节点数之多,用Floyed是求最小环是不明智的,那么可以视为费用流的问题解决;

建图:把每个点拆成两点建成有向图,然后增加一个超级源点s,和一个超级汇点t,s连接1,流量为2,费用为0,n连接t流量为2费用为0;

代码:

#include<iostream> #include<queue> using namespace std ; const int N=1110,M=10001,Inf=1<<30; struct Node { int vdx,flow,cost,next ; }Edge[10*M]; int n,m,k,maxflow,index[N],road[N],pre[N]; void add(int from,int to,int flow,int cost) { Edge[k].vdx = to ; Edge[k].flow = flow ; Edge[k].cost = cost ; Edge[k].next = index[from] ; index[from] = k++ ; Edge[k].vdx = from ; Edge[k].flow = 0 ; Edge[k].cost = -cost ; Edge[k].next = index[to] ; index[to] = k++ ; } bool spfa() { int next,visit[N],d[N]; for(int i=0;i<=N;i++) visit[i]=0,d[i]=Inf ; visit[0]=1,d[0]=0; queue<int>q; q.push(0); while(!q.empty()) { int cur=q.front(); q.pop(); visit[cur]=0; next=index[cur]; while(next!=-1) { if(Edge[next].flow>0&&d[cur]+Edge[next].cost<d[Edge[next].vdx]) { d[Edge[next].vdx] = d[cur]+Edge[next].cost ; road[Edge[next].vdx]=next; pre[Edge[next].vdx]=cur; if(!visit[Edge[next].vdx]) { visit[Edge[next].vdx]=1; q.push(Edge[next].vdx); } } next=Edge[next].next; } } if(d[n+1]==Inf) return false ; return true ; } void argue() { int minflow=Inf ; for(int v=n+1;v!=0;v=pre[v]) minflow=min(minflow,Edge[road[v]].flow); for(int v=n+1;v!=0;v=pre[v]) { maxflow+=minflow*Edge[road[v]].cost; Edge[road[v]].flow -=minflow ; Edge[road[v]^1].flow +=minflow ; } } int main() { while(scanf("%d%d",&n,&m)!=EOF) { int from,to,cost ; k = 0 ; memset(index,-1,sizeof(index)); for(int i=0;i<m;i++) { scanf("%d%d%d",&from,&to,&cost); add(from,to,1,cost) ; add(to,from,1,cost) ; } add(0,1,2,0) ; add(n,n+1,2,0) ; maxflow=0; while(spfa()) argue() ; printf("%d/n",maxflow); } return 0 ; }

 

 

ZJU http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3308

题意:国际象棋中,棋盘是黑白相间的格子组成,有些黑色格子上有金骑士,银骑士,铜骑士,每个骑士的走法和中国象棋的马走法一样,但是每个骑士的移动会消耗能量,金骑士移动一下消耗的能量为两个格子能量的最大值,银骑士消耗的能量为最格子能量的和,而铜骑士消耗的能量为两个能量的乘积,并且每个骑士最多可以移动一次,每个格子上最多只能有一个骑士,问:

能不能找到这样的一种是消耗能量最少的方法,要是能找到输出最小值;

要是不能输出“-1”;

黑白色建图;增加一源点s,汇点t,超级源点ss,超级汇点tt,ss->s,t->tt,流量为k,费用为0;

代码:

  #include<iostream> #include<queue> #include<stdio.h> #include<string.h> using namespace std ; const int N = 50 , M = 200001 , Inf = 999999999 ; int r,c,n,k,en,s,t,ss,tt,val[N][N],knight[N][N],indx[M],road[M],pre[M]; int _x[] = {-1,-2,-2,-1,1,2,2,1}; int _y[] = {-2,-1,1,2,2,1,-1,-2}; struct Node { int vdx,flow,cost,next; }Edge[M*2]; void add(int from,int to,int cost,int flow) { Edge[en].vdx = to ; Edge[en].flow= flow ; Edge[en].cost= cost ; Edge[en].next= indx[from] ; indx[from] = en ; en++ ; Edge[en].vdx = from ; Edge[en].flow= 0 ; Edge[en].cost= - cost ; Edge[en].next= indx[to] ; indx[to] = en ; en++ ; } bool spfa() { int visit[M],d[M]; queue<int>q; for(int i=0;i<=tt;i++) d[i]=Inf ; memset(visit,0,sizeof(visit)); visit[ss]=1,d[ss]=0; q.push(ss); while(!q.empty()) { int cur=q.front(); q.pop(); visit[cur]=0; int next=indx[cur]; while(next!=-1) { if(Edge[next].flow>0&&d[Edge[next].vdx]>d[cur]+Edge[next].cost) { d[Edge[next].vdx] = d[cur]+Edge[next].cost ; pre[Edge[next].vdx] = cur ; road[Edge[next].vdx]= next; if(0==visit[Edge[next].vdx]) { visit[Edge[next].vdx] = 1 ; q.push(Edge[next].vdx); } } next=Edge[next].next; } } if(d[tt]==Inf) return false ; return true ; } int maxflow,Time; void argue() { int minflow=Inf; for(int v=tt;v!=ss;v=pre[v]) minflow = min(minflow,Edge[road[v]].flow); Time ++ ; for(int v=tt;v!=ss;v=pre[v]) { maxflow += minflow*Edge[road[v]].cost; Edge[road[v]].flow -= minflow ; Edge[road[v]^1].flow += minflow ; } } int main() { while(scanf("%d%d%d%d",&r,&c,&n,&k)!=EOF) { for(int i=0;i<r;i++) for(int j=0;j<c;j++) scanf("%d",&val[i][j]); memset(knight,0,sizeof(knight)); int v,a,b; en=0; for(int i=0;i<n;i++) { scanf("%d%d%d",&v,&a,&b); knight[a-1][b-1]=v; } s=r*c+10,t=s+1,ss=t+1,tt=ss+1; memset(indx,-1,sizeof(indx)); for(int i=0;i<r;i++) { for(int j=0;j<c;j++) { int id1=i*c+j ; if((i+j)%2) { add(id1,t,0,1); continue ; } else { if(knight[i][j]==0) continue ; add(s,id1,0,1); for(int h=0;h<8;h++) { int x=i+_x[h] ; int y=j+_y[h] ; if(x>=0&&x<r&&y>=0&&y<c) { int id2=x*c+y; if(knight[i][j]==3) add(id1,id2,max(val[i][j],val[x][y]),1); else if(knight[i][j]==2) add(id1,id2,val[i][j]+val[x][y],1); else if(knight[i][j]==1) add(id1,id2,val[i][j]*val[x][y],1); } } } } } add(ss,s,0,k); add(t,tt,0,k); maxflow=0,Time=0; while(spfa()) argue(); if(Time==k) printf("%d/n",maxflow); else printf("-1/n"); } return 0 ; }        

 

你可能感兴趣的:(c,struct,System,ini)