[codevs 1227] 方格取數 2

whisperrr發表於2021-09-09

  題目描述 Description

  給出一個n*n的矩陣,每一格有一個非負整數Aij,(Aij <= 1000)現在從(1,1)出發,可以往右或者往下走,最後到達(n,n),每達到一格,把該格子的數取出來,該格子的數就變成0,這樣一共走K次,現在要求K次所達到的方格的數的和最大

  輸入描述 Input Description

  第一行兩個數n,k(1<=n<=50, 0<=k<=10)

  接下來n行,每行n個數,分別表示矩陣的每個格子的數

  輸出描述 Output Description

  一個數,為最大值

  樣例輸入 Sample Input

  3 1

  1 2 3

  0 2 1

  1 4 2

  樣例輸出 Sample Output

  11

  資料範圍及提示 Data Size & Hint

  1<=n<=50, 0<=k<=10

  這是我學了網路流之後自己寫的第一道建模題,雖說這道題也是一道很普通的題。

  建圖如下:很顯然想到把每個點當成點,向下面的點和右邊的點連邊,至於權值的事情,那就把一個點拆成兩個點,分為上點和下點,上點向下點連一條費用為該點點權,容量為1的邊(因為每個數只能取一次),但發現取完這個數後這個點還是可以走的,那就讓上點再連一條費用為0,容量為正無窮的邊(表示可以走很多次)到下點。匯點顯然是最後一個點的下點,至於源點的設定,因為題目中有走k次的要求,那就新創一個點,連向起點的上點,費用為0,容量為K,因為每一次取容量為1,所以取K次的意思就相當於走k條增廣路。然後就求一遍最大費用流即可。建圖還有一個比較煩的就是每個點編號的編排,具體編排方法下文註釋上有。

  1 #include

  2 #include

  3 #include

  4 #include

  5 #include

  6 #include

  7 using namespace std;

  8 typedef long long LL;

  9 inline int read()

  10 {

  11 int x=0,f=1;char c=getchar();

  12 while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}

  13 while(isdigit(c)){x=x*10+c-'0';c=getchar();}

  14 return x*f;

  15 }

  16 const int oo=2147000000;

  17 const int maxn=100000;

  18 struct Edge

  19 {

  20 int u,v,f,w,next;

  21 Edge() {}

  22 Edge(int _1,int _2,int _3,int _4,int _5) : u(_1),v(_2),f(_3),w(_4),next(_5) {}

  23 }e[maxn];

  24 int n,k,first[maxn],vis[maxn],dis[maxn],a[100][100],cnt,s,t,ans;

  25 queue Q;

  26 void add(int i,int a,int b,int c,int d)

  27 {

  28 e[i]=Edge(a,b,c,d,first[a]);

  29 first[a]=i;

  30 }

  31 void addEdge(int i,int a,int b,int c,int d){add(2*i,a,b,c,d);add(2*i+1,b,a,0,-d);}

  32 bool spfa()

  33 {

  34 memset(vis,0,sizeof(vis));

  35 for(int i=0;i<=2*n*n;i++)dis[i]=-oo;

  36 while(Q.size())Q.pop();

  37 dis[t]=0;vis[t]=1;Q.push(t);

  38 while(Q.size())

  39 {   大連婦科醫院哪個好  

    40 int now=Q.front();Q.pop();

  41 for(int i=first[now];i!=-1;i=e[i].next)

  42 if(dis[now]+e[i^1].w>dis[e[i].v] && e[i^1].f)

  43 {

  44 dis[e[i].v]=dis[now]+e[i^1].w;

  45 if(!vis[e[i].v])

  46 {

  47 vis[e[i].v]=1;

  48 Q.push(e[i].v);

  49 }

  50 }

  51 vis[now]=0;

  52 }

  53 return dis[s]!=-oo;

  54 }

  55 int dfs(int x,int flow)

  56 {

  57 vis[x]=1;

  58 if(x==t)return flow;

  59 int now,used=0;

  60 for(int i=first[x];i!=-1;i=e[i].next)

  61 if(dis[e[i].v]==dis[x]-e[i].w && e[i].f && !vis[e[i].v])

  62 {

  63 now=flow-used;

  64 now=dfs(e[i].v,min(now,e[i].f));

  65 ans+=now*e[i].w;

  66 e[i].f-=now;e[i^1].f+=now;

  67 used+=now;

  68 if(used==flow)return flow;

  69 }

  70 return used;

  71 }

  72 void zkw()

  73 {

  74 while(spfa())

  75 {

  76 vis[t]=1;

  77 while(vis[t])

  78 {

  79 memset(vis,0,sizeof(vis));

  80 dfs(s,oo);

  81 }

  82 }

  83 }

  84 int main()

  85 {

  86 memset(first,-1,sizeof(first));

  87 n=read();k=read();

  88 for(int i=0;i

  89 for(int i=0;i

  90 for(int j=0;j

  91 {

  92 addEdge(cnt,2*(i*n+j),2*(i*n+j)+1,1,a[i][j]);cnt++;

  93 addEdge(cnt,2*(i*n+j),2*(i*n+j)+1,oo,0);cnt++;

  94 if(j!=n-1)addEdge(cnt,2*(i*n+j)+1,2*(i*n+j+1),oo,0);cnt++;//向右邊

  95 if(i!=n-1)addEdge(cnt,2*(i*n+j)+1,2*((i+1)*n+j),oo,0);cnt++;//下面

  96 }

  97 addEdge(cnt,2*n*n,0,k,0);cnt++;

  98 s=2*n*n;t=2*n*n-1;

  99 zkw();

  100 printf("%d",ans);

  101 return 0;

  102 }

  103 /*

  104 建圖:

  105 原圖中a[i][j],對應編號為i*n+j,拆成兩個點,一個是2*(i*n+j),另一個是2*(i*n+j)+1

  106 (i,j)上點向下點連兩條邊,下點向下面和右面連一條邊

  107 源點 2*n*n 向0,0連一條邊

  108 匯點 2*n*n-1

  109 */


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70005147/viewspace-2791285/,如需轉載,請註明出處,否則將追究法律責任。

相關文章