【BZOJ-1565】植物大戰殭屍 拓撲排序 + 最小割

weixin_30639719發表於2020-04-05

1565: [NOI2009]植物大戰殭屍

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1972  Solved: 917
[Submit][Status][Discuss]

Description

Input

Output

僅包含一個整數,表示可以獲得的最大能源收入。注意,你也可以選擇不進行任何攻擊,這樣能源收入為0。

Sample Input

3 2
10 0
20 0
-10 0
-5 1 0 0
100 1 2 1
100 0

Sample Output

25

HINT

在樣例中, 植物P1,1可以攻擊位置(0,0), P2, 0可以攻擊位置(2,1)。 
一個方案為,首先進攻P1,1, P0,1,此時可以攻擊P0,0 。共得到能源收益為(-5)+20+10 = 25。注意, 位置(2,1)被植物P2,0保護,所以無法攻擊第2行中的任何植物。 
【大致資料規模】
約20%的資料滿足1 ≤ N, M ≤ 5;
約40%的資料滿足1 ≤ N, M ≤ 10;
約100%的資料滿足1 ≤ N ≤ 20,1 ≤ M ≤ 30,-10000 ≤ Score ≤ 10000 。

Source

Solution

一開始想到的是,在網格圖上加限制然後搞搞,發現其實不對,因為能吃掉的植物和網格什麼的無關,而是和植物們的攻擊有關

很容易想到,如果一個植物被另一個植物保護,那麼必須吃掉另一個植物,才能吃掉這個植物,所以,很顯然順序是需要考慮的,但又發現,如果多個植物可以互相保護(也就是連成環,很顯然這些植物都是無敵的RMB玩家)

這種型別很容易就聯想到最小割模型(最大權閉合子圖),又因為環上植物無敵,所以不去考慮環上植物,那麼把環上的扔掉即可

開始想寫Tarjan,寫完後發現不行,於是換拓撲排序,然後發現太久太久沒寫過了,竟然快忘了....

這裡可以直接建網路流圖,拓撲一遍,在增廣的時候加限制即可.(不過需要一些特殊的技巧)

或者先對保護的點連邊,拓撲一遍,然後再建新圖跑網路流即可 值得注意的是:保護的點需要加上它左邊的點(很顯然殭屍不能跳過一個植物吃後面的)

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define maxn 1000
#define maxm 1000010
int n,m,total,score[maxn];
struct EdgeNode{int next,to,cap;}edge[maxm];
int head[maxn],cnt=1;
void addedge(int u,int v,int w) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].cap=w;}
void insertedge(int u,int v,int w) {addedge(u,v,w); addedge(v,u,0);}

struct RoadNode{int next,to;}road[maxm>>1];
int last[maxn],tot;
void addroad(int u,int v) {tot++; road[tot].next=last[u]; last[u]=tot; road[tot].to=v;}
void insertroad(int u,int v) {addroad(u,v);}
bool visit[maxn]; int du[maxn],s,t;
void Paint(int now)
{
    visit[now]=1;
    for (int i=last[now]; i; i=road[i].next)
        if (!visit[road[i].to]) Paint(road[i].to);
}
void TopoSort()
{
    s=1,t=n*m;
    stack<int>st;
    for (int i=s; i<=t; i++) if (!du[i]) st.push(i); else visit[i]=1;
    while (!st.empty())
        {
            int now=st.top(); st.pop(); visit[now]=0;
            for (int i=last[now]; i; i=road[i].next)
                {
                    du[road[i].to]--;
                    if (!du[road[i].to]) st.push(road[i].to);
                }
            //printf("%d\n",now);
        }
    for (int i=s; i<=t; i++) if (visit[i]) Paint(i);
}
int dis[maxn],cur[maxn],S,T;
#define inf 0x7fffffff
bool bfs()
{
    queue<int>q;
    for (int i=S; i<=T; i++) dis[i]=-1;
    q.push(S); dis[S]=0;
    while (!q.empty())
        {
            int now=q.front(); q.pop();
            for (int i=head[now]; i; i=edge[i].next)
                if (edge[i].cap && dis[edge[i].to]==-1)
                    dis[edge[i].to]=dis[now]+1,q.push(edge[i].to);
        }
    return dis[T]!=-1;
}
int dfs(int loc,int low)
{
    if (loc==T) return low;
    int w,used=0;
    for (int i=cur[loc]; i; i=edge[i].next)
        if (edge[i].cap && dis[edge[i].to]==dis[loc]+1)
            {
                w=dfs(edge[i].to,min(low-used,edge[i].cap));
                edge[i].cap-=w; edge[i^1].cap+=w; used+=w;
                if (used==low) return low; if (edge[i].cap) cur[loc]=i;
            }
    if (!used) dis[loc]=-1;
    return used;
}
int dinic()
{
    int tmp=0;
    while (bfs())
        {
            for (int i=S; i<=T; i++) cur[i]=head[i];
            tmp+=dfs(S,inf);
        }
    return tmp;
}
void Build()
{
    S=0,T=n*m+1;
    for (int i=1; i<=n*m; i++)
        if (!visit[i])
            {
                if (score[i]>0) insertedge(i,T,score[i]); else insertedge(S,i,-score[i]);
                for (int j=last[i]; j; j=road[j].next)
                    if (!visit[road[j].to]) insertedge(i,road[j].to,inf);
                total+=score[i]>0?score[i]:0;
            }
}
int locate(int x,int y) {return (x-1)*m+y;}
int main()
{
    n=read(),m=read();
    for (int i=1; i<=n; i++)
        for (int j=1; j<=m; j++)
            {
                int now=locate(i,j),num;
                score[now]=read(); num=read();
                for (int x,y,k=1; k<=num; k++)
                    x=read()+1,y=read()+1,insertroad(now,locate(x,y)),du[locate(x,y)]++;
                if (j>1) insertroad(now,locate(i,j-1)),du[locate(i,j-1)]++;
            }
    TopoSort();
    Build();
    int maxflow=dinic();// printf("%d %d\n",total,maxflow);
    printf("%d\n",total-maxflow);
    return 0;
}

一寫這種需要重構圖的,用上road啊last,tot啊什麼的,總會莫名其妙的寫混...還要肉眼對拍浪費時間

轉載於:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5516268.html

相關文章