【Usaco2008 Oct 资格赛】灌水 和 Islands and Bridges

Description

Farmer John已经决定把水灌到他的n(1<=n<=300)块农田,农田被数字1到n标记。把一块土地进行灌水有两种方法,从其他农田饮水,或者这块土地建造水库。
建造一个水库需要花费wi(1<=wi<=100000),连接两块土地需要花费Pij(1<=pij<=100000,pij=pji,pii=0).
计算Farmer John所需的最少代价。

Input

*第一行:一个数n
*第二行到第n+1行:第i+1行含有一个数wi
*第n+2行到第2n+1行:第n+1+i行有n个被空格分开的数,第j个数代表pij。

Output

*第一行:一个单独的数代表最小代价.

分析

其实就是类似于要求选点使得这个图连通的选法中的代价最小,所以是最小生成树,
当然还要在用0向每个点连一条每个单独的费用,即可解决这个问题了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
const int N=305;
struct note{
    ll a,b,c;
}a[N*N];
ll x,n,fa[N],gx,gy;
bool cmp(note x,note y){
    return x.c<y.c;
}
int getfa(int x){
    if (!fa[x]) return x;
    else {
        fa[x]=getfa(fa[x]);
        return fa[x];
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        a[i].a=0;
        a[i].b=i;
        a[i].c=x;
    }
    int tot=n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=i;j++) scanf("%d",&x);
       for(int j=i+1;j<=n;j++){
           scanf("%d",&x);
           a[++tot].a=i;
           a[tot].b=j;a[tot].c=x;
       }
       scanf("\n");
    }
    sort(a+1,a+tot+1,cmp);
    ll ans=0;
    for(int i=1;i<=tot;i++){
        gx=getfa(a[i].a);gy=getfa(a[i].b);
        if (gx!=gy){
            ans+=a[i].c;
            fa[gx]=gy;
        }
    }
    printf("%d",ans);
}

Islands and Bridges

Description

  给定一些岛屿和一些连接岛屿的桥梁,大家都知道汉密尔顿路是访问每个岛屿一次的路线,在我们这个地图中,每个岛屿有个正整数的权值,表示这个岛屿的观赏价值。假设一共有N个岛屿,用Vi表示岛屿Ci的价值,汉密尔顿路C1C2….Cn的价值是以下三部分的总和:
  (1)所有岛屿的价值之和;
  (2)对于路径中相邻的两个岛屿CiCi+1,把两个岛屿的价值之积加到总价值中;
  (3)路径中连续三个岛屿CiCi+1Ci+2,如果Ci与Ci+2有桥直接相连,则把这三个岛屿价值之积加到总价值中。
  要求计算汉密尔顿路最大的价值以及方案数。
 Input

  输入第一行是一个整数Q(Q<=20),表示测试数据的数量。每个测试数据第一行输入两个整数N和M,分别表示岛屿数和桥梁数,接下来一行包含N个正整数,第i个数表示Vi,每个数不超过100,最后M行,每行两个数X,Y,表示岛X和岛Y之间有一座桥直接相连,桥是双向的,岛屿编号为1到N(N<=13)

Output

  对于每个测试数据,输出一行,两个整数,第一个数表示最大价值,第二个数表示方案数,如果不存在汉密尔顿路径,输出“0 0”
  注意:一条路径可以反着走,我们认为这两条路径是同一条路径。

分析

这题比较简单,就不多说了。
设f[i][j][k]表示i为状态,最后的是j,倒数第二的是k的最大值。
直接维护即可。
但是注意开long long
而且要注意特殊情况,n=1
所以这考的是细心和耐心,而若不细心就会前功尽弃了,白白打这程序了

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=14;
ll t,n,m,x,y,a[N][N];
ll ans,sum,num[N][N],f[1<<N][N][N],g[1<<N][N][N],no,v[N],c[N+2]; 
int main(){
    scanf("%d",&t);
    c[0]=1;
    for(int i=1;i<=N;i++)c[i]=c[i-1]*2;
    for(;t;t--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) a[i][0]=0;
        memset(num,0,sizeof(num));
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        for(int i=1;i<=n;i++) scanf("%d",&v[i]);
        for(int i=1;i<=m;i++){
            scanf("%d %d",&x,&y);
            if (x==y) continue;
            if (!num[x][y]) a[x][++a[x][0]]=y,a[y][++a[y][0]]=x;
            num[x][y]++;num[y][x]++;
        }
        for(int i=1;i<=n;i++)
           for(int j=1,k=a[i][1];j<=a[i][0];j++,k=a[i][j]){
                int toe=(1<<(i-1))+(1<<(a[i][j]-1));
               f[toe][i][k]=v[k]*v[i]+v[i]+v[k],
               g[toe][i][k]=num[k][i];
           }
        for(int i=0;i<=c[n]-1;i++)
           for(int st=1;st<=n;st++)
           if (i&(c[st-1]))
              for(int en=1;en<=n;en++)
              if (i&(c[en-1])&&(g[i][st][en])){
                  for(int k=1,j=a[en][1];k<=a[en][0];k++,j=a[en][k]){
                        if (!(i&c[j-1])){
                            int oi=f[i+c[j-1]][en][j];
                            if (num[st][j]) no=v[j]*v[st]*v[en];else no=0;
                            if (oi<f[i][st][en]+v[j]+no+v[j]*v[en]){
                                f[i+c[j-1]][en][j]=f[i][st][en]+v[j]+no+v[j]*v[en];
                                g[i+c[j-1]][en][j]=g[i][st][en]*num[j][en];
                            }else if (oi==f[i][st][en]+v[j]+no+v[j]*v[en]){
                                g[i+c[j-1]][en][j]+=g[i][st][en]*num[j][en];
                            }
                        }
                    }
              }
        ans=0,sum=0;
        for(int i=1;i<=n;i++)
           for(int j=1;j<=n;j++)
           if (g[c[n]-1][i][j]){
               if (ans<f[c[n]-1][i][j]) 
                  ans=f[c[n]-1][i][j],sum=g[c[n]-1][i][j];
               else if (ans==f[c[n]-1][i][j]) 
                  sum+=g[c[n]-1][i][j];
           }
        if (n==1) {
            printf("%d 1\n",v[1]);
        }else 
        printf("%lld %lld\n",ans,sum/2);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值