185. [USACO Oct08] 挖水井

185. [USACO Oct08] 挖水井

★★   输入文件:water.in   输出文件:water.out   简单对比
时间限制:1 s   内存限制:128 MB

农夫约翰决定给他的N(1<=N<=300)个牧场浇水,这些牧场被自然的命名为1..N。他可以给一个牧场引入水通过在这个牧场挖一口井或者修一条管道使这个牧场和一个已经有水的牧场连接。

在牧场i挖一口井的花费是w_i(1<=w_i<=100000)。修建一条水管连接牧场i和牧场j的花费是p_ij(1<=p_ij<=100000;p_ij=p_ji;p_ii=0)。

请确定农夫约翰为了完成浇灌所有的牧场所需的最小的总花费。

题目名称:water

输入格式:

  • 第1行:一个单独的整数n。
  • 第2..n+1行:第i+1行包含一个单独的整数w_i。
  • 第n+2..2n+1行:第n+1+i行包含n个用空可分开的整数;其中第j个数是p_ij。

输入样例(file water.in):

4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0

输入说明:

这里有4个牧场,修井和修管道的代价如图。

输出格式:

  • 第1行:一个单独的整数,表示花费。

输出样例(file water.out):

9

输出说明:

农夫约翰可以在第4个牧场修井,并且将每个牧场和第一个连接起来,这样,花费是3+2+2+2=9。

思路:一看到最小花费,就应该想到Kruskal算法,但是这个题有一个不同的地方就是多了一个挖水井的花费,这样的话我们要想用Kruskal算法解题就应该把他挖水井所用的花费一起加入到边中。我们可以将第i条边接到第n+1(这个本身不存在)条边上,这样在来跑Kruskal,跑出来的就一定是最小话费

    其实这道题理论上用贪心也可以,我一开始就是这么做的,无奈贪心的时候逻辑太混乱,贪到最后都不知道应该贪谁了。。。。。(但自测贪心可以过n<=4的所有情况)、

 

错因:1.思路没打开

   2.贪心没贪好

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int MAXN=100001;
 7 const int maxn=0x7fffffff;
 8 int n;
 9 int w[MAXN];
10 int f[MAXN];
11 struct node
12 {
13     int u;
14     int v;
15     int w;
16 }edge[MAXN];
17 int num=1;
18 int comp(const node & a,const node & b)
19 {
20     if(a.w<b.w)
21     return 1;
22     else 
23     return 0;
24 }
25 int find(int x)
26 {
27     if(f[x]!=x)
28     f[x]=find(f[x]);
29     return f[x];
30 }
31 void unionn(int x,int y)
32 {
33     int fx=find(x);
34     int fy=find(y);
35     f[fx]=fy;
36 }
37 int main()
38 {
39     freopen("water.in","r",stdin);
40     freopen("water.out","w",stdout);
41     scanf("%d",&n);
42     for(int i=1;i<=n;i++)f[i]=i;
43     for(int i=1;i<=n;i++)
44         scanf("%d",&w[i]);
45     for(int i=1;i<=n;i++)
46     {
47         for(int j=1;j<=n;j++)
48         {
49             scanf("%d",&edge[num].w);
50             edge[num].u=i;
51             edge[num].v=j;
52             num++;
53         }
54     }
55     for(int i=1;i<=n;i++)//  将挖水井所需要的花费做成一条从i到n+1的边 
56     {
57         edge[num].u=i;
58         edge[num].v=n+1;
59         edge[num].w=w[i];
60         num++;
61         edge[num].u=edge[num-1].v;
62         edge[num].v=edge[num-1].u;
63         edge[num].w=w[i];
64         num++;
65     }
66     sort(edge+1,edge+num,comp);
67     int k=0;
68     long long int tot=0;
69     for(int i=1;i<=num;i++)
70     {
71         if(find(edge[i].u)!=find(edge[i].v))
72         {
73             unionn(edge[i].u,edge[i].v);
74             tot=tot+edge[i].w;
75             k++;
76         }
77         if(k==n)
78         break;
79     }
80     printf("%lld",tot);
81     return 0;
82 }

 

 

贪心代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<cmath>
  6 #include<queue>
  7 #include<stack>
  8 using namespace std;
  9 const int MAXN=100001;
 10 const int maxn=0x7fffffff;
 11 int wa[MAXN];
 12 int vis[MAXN];// 记录每个点是否已经被挖 
 13 int map[1001][1001];
 14 struct node
 15 {
 16     int u;
 17     int v;
 18     int w;
 19     int next;
 20 }edge[MAXN]; 
 21 int num=1;
 22 int head[MAXN];
 23 long long int tot;// 结果 
 24 int k;// 建立树 需要的边数 
 25 int n;
 26 int flag2=0;//记录到底有没有挖坑 
 27 int kcomp(const node & a,const node & b)
 28 {
 29     if(a.w<b.w)
 30     return 1;
 31     else return 0;
 32 }
 33 void find()//  找出不需要参与建立最小生成树的边 并删除 
 34 {
 35     for(int i=1;i<=n;i++)
 36     {
 37         int flag=1;// 默认需要挖 
 38         for(int j=head[i];j!=-1;j=edge[j].next)
 39         {
 40             if(edge[j].w<wa[i]&&edge[j].u!=edge[j].v)
 41             {
 42                 flag=0;
 43                 break;
 44             }
 45         }
 46         if(flag==1)
 47         {
 48             if(flag2==1)k--;
 49             flag2=1;
 50             tot=tot+wa[i];
 51             wa[i]=maxn;// 删除这个点 
 52             vis[i]=1;
 53             
 54         }
 55     }
 56 } 
 57 void kruskal()
 58 {
 59     for(int i=1;i<=num;i++)
 60     {
 61         if(k==0)break;
 62         if((vis[edge[i].u]==0||vis[edge[i].v]==0)||(map[edge[i].u][edge[i].v]==0))
 63         {
 64             if(edge[i].v<=edge[i].u)
 65             continue;
 66             if(vis[edge[i].u]==0)
 67             vis[edge[i].u]=1;
 68             else if(vis[edge[i].v]==0)
 69             vis[edge[i].v]=1;
 70             map[edge[i].u][edge[i].v]=1;
 71             map[edge[i].v][edge[i].u]=1;
 72             tot=tot+edge[i].w;
 73             k--;
 74         }
 75     }
 76 }
 77 int main()
 78 {
 79     freopen("water.in","r",stdin);
 80     freopen("water.out","w",stdout);
 81     scanf("%d",&n);
 82     for(int i=1;i<=n;i++)head[i]=-1;
 83     k=n-1;
 84     for(int i=1;i<=n;i++)
 85         scanf("%d",&wa[i]);
 86     for(int i=1;i<=n;i++)
 87     {
 88         for(int j=1;j<=n;j++)
 89         {
 90             scanf("%d",&edge[num].w);
 91             edge[num].u=i;
 92             edge[num].v=j;
 93             edge[num].next=head[i];
 94             head[i]=num++;
 95         }
 96     } 
 97     find();//  找出不需要参与建立最小生成树的边 
 98     sort(edge+1,edge+num,kcomp);
 99     kruskal();// 用剩余的边建立最小生成树 
100     sort(wa+1,wa+1+n);
101     if(wa[1]!=maxn&&flag2==0)
102     tot=tot+wa[1];
103     printf("%lld",tot);
104     fclose(stdin);
105     fclose(stdout);
106     return 0;
107 }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值