(待切)POJ 1926 Pollution 模拟/并查集

Pollution
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 3904 Accepted: 1034

Description

The managers of a chemical plant, which is notorious for its high pollution, plan to adopt a newly developed device in order to reduce the amount of contaminants emitted. However, engineers in the plant are against this plan, as they doubt the usefulness of the device. As engineers only believe in experimental results, managers decide to hire programmers to make a numerical experiment to convince the engineers. 

The new pollution-reducing device consists of several tanks with pipes connecting the tanks. You may assume there is at most one pipe between two tanks. Two tanks are called adjacent if a pipe connects them. When operating, the contaminant circulates in the device among these tanks. 

As shown in the Figure-1, the contaminant in one tank in time t, will equally distribute into all adjacent tanks in the time t+1. In other words, if we use X i t to denote the amount of contaminant in tank i at time t, we can use the following formula: 


where I ij=1 if tank i and tank j are adjacent, otherwise I ij=0, and where d j is the number of tanks adjacent to tank j. If no tank is adjacent to tank i, we have X i t+1=X i t
The managers, as well as the engineers, want to know that given the initial amount of contaminant in each tank, how the contaminant will be distributed in all the tanks after a long period of time in circulation. Namely, given X i 0 for all i, what are X i t when the difference between X i t and X i t+1 is so small that it can be ignored. You may assume that this condition will ALWAYS be attained from an initial case in this problem.

Input

The first line of the input contains one integer T (1 <= T <= 10), the number of test cases. T cases then follow. For each test case, the first line consists of two integers: N and M where(1 <= N <= 100, 0 <= M <= N*(N-1)/2), is the number of tanks and pipes. The following N lines give the initial amount of contaminant for each tank, which are nonnegative real numbers and no larger than 100. Then the next M lines give the tanks that each pipe connects, as "A B" (1 <= A, B <= N, A != B) denotes there is a pipe between tank A and tank B.

Output

For each test case, output the final amount of contaminant X i t+1 (one per line), followed by a blank line. The number should be rounded to three digits after the decimal point.

Sample Input

2
3 3
1
0
0
1 2
2 3
3 1
4 4
1
0
0
1
1 2
2 3
3 1
3 4

Sample Output

0.333
0.333
0.333

0.500
0.500
0.750
0.250

题目大意:

给n个点和m条边,两点间最多只有一条边(可以没有)

每个点有一个正实数的起始量,每个点在每一次转移时会将其流量全部流光,规则是平分流向所有与其相邻的点


给出一个起始状态,求出平衡后的个点量。


解题思路:

(1)看了看时限,觉得完全可以模拟。但是模拟就是一直WA 啊啊啊啊啊!!!真是不明白

这是WA了模拟代码,以后有空再回来切

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define maxn 105
#define maxe 5005
#define eps 1e-8

using namespace std;

int si,head[maxn],num[maxn];
int t,n,m;
struct edge{int to,next;} G[2*maxe];
double vol[maxn][2];

void addedge(int from,int to)
{
    num[from]+=1;
    G[si].to=to;
    G[si].next=head[from];
    head[from]=si++;
}

bool check(int pre,int now)
{
    bool flag=false;
    for(int i=1;i<=n;i+=1){
        if(fabs(vol[i][pre]-vol[i][now])>=eps) flag=true;
    }
    return flag;
}

int main()
{
    cin>>t;
    while(t--){
        si=0;
        memset(head,-1,sizeof head);
        memset(vol,0,sizeof vol);
        memset(num,0,sizeof num);
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i+=1) cin>>vol[i][0];
        for(int i=0;i<m;i+=1){
            int from,to;
            scanf("%d %d",&from,&to);
            addedge(from,to);
            addedge(to,from);
        }
        /*for(int i=1;i<=n;i+=1){
            cout<<i<<"______";
            for(int j=head[i];j!=-1;j=G[j].next){
                cout<<" "<<G[j].to<<endl;
            }
        }*/

        int countt=1;
        while(1){
            for(int i=1;i<=n;i+=1){
                double ave=vol[i][(countt-1)%2]/num[i];
                for(int j=head[i];j!=-1;j=G[j].next){
                    vol[G[j].to][countt%2]+=ave;
                    //cout<<"_"<<i<<" "<<G[j].to<<" "<<vol[G[j].to][countt%2]<<endl;
                    //system("pause");
                }
            }
            if(!check((countt-1)%2,countt%2)) break;

            for(int i=1;i<=n;i+=1){
                vol[i][(countt-1)%2]=0;
            }
            countt+=1;
        }

        for(int i=1;i<=n;i+=1){
            printf("%.3lf\n",vol[i][(countt)%2]);
        }
        cout<<endl;
    }

    return 0;
}




(2)上网看了其他人的算法。有这么一个规律,在一个连通图中,平衡后的某个点的量在这个连通图总量的比重=其度数与这个图总度数的比值。

这么一来就是可以用并查集,划分好连通图,算出总度数和总流量,用比例直接算出答案了。


下面是自己写的ac代码,但是有个地方不太明白,以后回来再看看吧


#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define maxn 105
#define maxe 5005
#define eps 1e-8

using namespace std;
int n,m;
int par[maxn];
int rank[maxn];
int degree[maxn],sumd[maxn];
double v[maxn],sumv[maxn];

void init()
{
    for(int i=1;i<=n;i+=1){
        par[i]=i;
        degree[i]=0;
        rank[i]=0;
        sumv[i]=0;
        sumd[i]=0;
    }
}

int find(int now)
{
    if(now==par[now]) return now;
    else return par[now]=find(par[now]);
}

void unite(int x,int y)
{
    degree[x]+=1,degree[y]+=1;
    x=find(x);
    y=find(y);
    if(x==y){
        sumd[x]+=2;
        return ;
    }
    //sumd[x]+=1;       // 这个函数里面的注释都是原来自己WA的写法
    //sumd[y]+=1;       // 这个函数里面的注释都是原来自己WA的写法

    if(rank[x]>rank[y]) par[y]=x,sumv[x]+=sumv[y]/*,sumd[x]+=sumd[y]*/,sumd[x]+=2;
      //把这四句都注释掉了之后再加上+=2的这两句就AC了  但是老觉得网上的人这么做是不对的  可是的确又ac了,想不明白
    else{
        sumv[y]+=sumv[x];
        par[x]=y;
        //sumd[y]+=sumd[x];
        sumd[y]+=2;
        if(rank[x]==rank[y]) rank[y]++;
    }
}


int main()
{
    int t;
    cin>>t;
    while(t--){
        scanf("%d %d",&n,&m);
        init();
        for(int i=1;i<=n;i+=1) {
            cin>>v[i];
            sumv[i]=v[i];
        }
        for(int i=0;i<m;i+=1){
            int from,to;
            scanf("%d %d",&from,&to);
            unite(from,to);
        }

        for(int i=1;i<=n;i+=1){
            if(!degree[i]) printf("%.3lf\n",v[i]);
            else printf("%.3lf\n",sumv[par[i]]*((double)degree[i]/(double)sumd[par[i]]));
        }
        cout<<endl;
    }

    return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值