poj3498:March of the Penguins——題解

最近的題解的故事背景割。

題目:
描述

在靠近南極的某處,一些企鵝站在許多漂浮的冰塊上。由於企鵝是羣居動物,所以它們想要聚集到一起,在同一個冰塊上。企鵝們不想把自己的身體弄溼,所以它們在冰塊之間跳躍,但是它們的跳躍距離有一個上限。
隨着氣溫的升高,冰塊開始融化,並出現了裂痕。而企鵝跳躍的壓力,使得冰塊的破裂加速。幸運的是,企鵝對冰塊十分有研究,它們能知道每塊冰塊最多能承受多少次跳躍。對冰塊的損害只在跳起的時候產生,而落地時並不對其產生傷害。
現在讓你來幫助企鵝選擇一個冰面使得它們可以聚集到一起。

輸入

第一行一個正數:數據個數,最多100個。之後:

第一行整數N,和浮點數D,表示冰塊的數目和企鵝的最大跳躍距離。
(1≤N ≤100) (0 ≤D ≤100 000),
接下來N行,xi, yi, ni and mi,分別表示冰塊的X和Y座標,該冰塊上的企鵝數目,以及還能承受起跳的次數。

輸出

每個數據:

輸出所有可能的相聚冰塊的編號,以0開始。如果不能相遇,輸出-1。

樣例輸入

2
5 3.5
1 1 1 1
2 3 0 1
3 5 1 1
5 1 1 1
5 4 0 1
3 1.1
-1 0 5 10
0 0 3 9
2 0 1 1
樣例輸出

1 2 4
-1

攢的有點多先寫一道。
簡單點說:網絡流的題,自己建一個源點,源點連向每個冰塊,容量爲該冰塊企鵝數。
冰塊分爲入點和出點,入點到出點容量爲起跳次數。
一個冰塊能跳到另一個冰塊,則前者出點連後者入點,容量INF,後者出點連前者入點,容量INF。
枚舉匯點。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=205;
const int INF=214748364;
struct{
    int next;
    int to;
    int w;
    int cun;
}edge[maxn*maxn];
int head[maxn],cnt=-1;
void add(int u,int v,int w){//u起點v終點w容量
    cnt++;
    edge[cnt].to=v;
    edge[cnt].w=w;
    edge[cnt].next=head[u];
    edge[cnt].cun=w;
    head[u]=cnt;
}
int lev[maxn],cur[maxn];//lev層數,cur[i]爲以i爲起點的邊的編號 
bool bfs(int m,int zong){//m爲匯點,有zong個點 
    int dui[zong+1],r=0;//隊列和右指針 
    for(int i=1;i<=zong;i++){//初始化 
        lev[i]=-1;
        cur[i]=head[i];
    }
    dui[0]=1,lev[1]=0;
    int u,v;//u起點v終點 
    for(int l=0;l<=r;l++){//左指針 
        u=dui[l];
        for(int e=head[u];e!=-1;e=edge[e].next){
            v=edge[e].to;
            if(edge[e].w>0&&lev[v]==-1){//1.能走 2.未分層 
                lev[v]=lev[u]+1;
                r++;
                dui[r]=v;//v入隊 
                if(v==m)return 1;//分層完畢 
            }
        }
    }
    return 0;//無法分層 
}
int dinic(int u,int flow,int m){//u當前點,flow爲下面的點能夠分配多大的流量,m終點 
    if(u==m)return flow;//終點直接全流入
    int res=0,delta;//res實際流量 
    for(int &e=cur[u];e!=-1;e=edge[e].next){//'&'相當於cur[u]=e;即流滿的邊不會再被掃一次 
        int v=edge[e].to;
        if(edge[e].w>0&&lev[u]<lev[v]){//只能從低層往高層流
            delta=dinic(v,min(edge[e].w,flow-res),m);
            if(delta>0){//如果增廣 
                edge[e].w-=delta;//正向邊容量減少 
                edge[e^1].w+=delta;//反向邊仍量增加(暗示退流) 
                res+=delta;//擴張流量增加 
                if(res==flow)break;//可流的都流完了,及時跳出 
            }
        }
    }
    if(res!=flow)lev[u]=-1;//沒流完,說明以後不能從這個點流出任何流量,那就不需要這個點了 
    return res;
}
double suan(int a1,int b1,int a2,int b2){
    return sqrt(pow((a2-a1),2)+pow((b2-b1),2));
}
int a[101],b[101],c[101],e[101];
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        double d;
        int sum=0;
        scanf("%d%lf",&n,&d);
        for(int i=1;i<=maxn;i++){
            head[i]=-1;
        }
        cnt=-1;
        for(int i=1;i<=n;i++){
            scanf("%d%d%d%d",&a[i],&b[i],&c[i],&e[i]);
            add(i*2,i*2+1,e[i]);
            add(i*2+1,i*2,0);
            sum+=c[i];
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(i==j)continue;
                if(suan(a[i],b[i],a[j],b[j])<=d){
                    add(i*2+1,j*2,INF);
                    add(j*2,i*2+1,0);
                }
            }
        }
        for(int i=1;i<=n;i++){
            if(c[i]!=0){
                add(1,i*2,c[i]);
                add(i*2,1,0);
            }
        }
        int ok=0;
        int first=0;
        for(int i=1;i<=n;i++){
            int zui=i*2;
            int ans=0;
            while(bfs(zui,2*n+1)==1){
                ans+=dinic(1,INF,zui);
            }
            if(ans==sum){
                if(first!=0)printf(" ");
                printf("%d",i-1);//編號以0開始所以-1(纔不是我懶得改了) 
                first=1;
                ok=1;
            }
            for(int i=0;i<=cnt;i++){
                edge[i].w=edge[i].cun;
            }
        }
        if(ok==0)printf("-1");
        printf("\n");
    } 
    return 0;
}
發佈了59 篇原創文章 · 獲贊 4 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章