USACO 2011 December Contest题解

 

USACO 2011 December Contest

Silver Division

Problem 1. CowPhotography

首先我们可以发现:若在大于等于3张照片中a牛都在b牛的前面,那么在初始状态中a牛就在b牛前面。

于是我们可以记下每张照片中每头牛的位置,然后在sort中加一个神奇的cmp函数(若a牛在2张以上的照片中在b牛前,返回1,否则返回0)

代码如下:

#include
using namespace std;

inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x;
}

map a[6];
int b[20001];

inline bool cmp(int x,int y){
    int s=0;
    for(int i=1;i<=5;++i) s+=a[i][x]     return s>2;
}

int main()
{
    //freopen("photo.in","r",stdin);
    //freopen("photo.out","w",stdout);
    int n=read();
    for(int i=1;i<=5;++i)
        for(int j=1;j<=n;++j){
            int x=read();
            a[i][x]=j;
            if(i<2) b[j]=x;
    }
    sort(b+1,b+n+1,cmp);
    for(int i=1;i<=n;++i) printf("%d\n",b[i]);
}

 

 

Problem 2. Roadblock

一道最短路题目。唯一特殊的是,要将一条边的长度*2,使1到n的最短路尽可能长。

考虑到:改变的边肯定在一开始的最短路中,否则改了也不影响最短路长度。

首先,肯定要求出一开始1到n的最短路,不过要记录下路径。然后枚举路径上的每条边,*2,跑最短路,比大小。

代码如下:

#include

using namespace std;

 

inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x;
}

int e[101][101],d[101],pre[101],n,m;
bool vis[101];

inline int bestpath(int t,int w){
    memset(d,63,sizeof(d));
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    d[t]=0;
    while(1){
        int close=-1;
        for(int i=1;i<=n;++i)
        if(!vis[i]&&(close==-1||d[i]             close=i;
        if(close==-1) break;
        vis[close]=1;
        for(int i=1;i<=n;++i){
            int x=d[close]+e[close][i];
            if(x                 d[i]=x;
                pre[i]=close;
           }
        }
    }
    return d[w];
}

int main()
{
    n=read(),m=read();
    memset(e,63,sizeof(e));
    for(int i=1;i<=m;++i){
        int u=read(),v=read(),w=read();
        e[u][v]=w,e[v][u]=w;
    }
    int s1=bestpath(1,n);
    vector v;
    for(int i=n;i!=-1;i=pre[i]) v.push_back(i);

    int s2=s1;
    for(int i=0;i+1         int a=v[i],b=v[i+1];
        e[a][b]*=2;
        e[b][a]*=2;
        s2=max(s2,bestpath(1,n));
        e[a][b]/=2;
        e[b][a]/=2;
    }
    printf("%d",s2-s1);
}

 

 

Problem 3. Umbrellas for Cows

一道dp题。首先,将牛排序。然后,因为题中说大伞可能比小伞便宜,我们可以从第二大的伞倒推,若比它大1的伞比它便宜,就将它的价格更新为大伞的价格。最后,n^2的dp。f[i]表示最后一把伞恰好挡住第i头牛时最少的花费。转移方程:f[i]=min(f[j]+c[a[i]-a[j+1]+1])(0

代码如下:

#include
using namespace std;

inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x;
}

int a[5001],c[100001],f[5001];
int main()
{
    int n=read(),m=read();
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=1;i<=m;++i) c[i]=read();
    sort(a+1,a+n+1);
    for(int i=m-1;i>=1;--i) c[i]=min(c[i],c[i+1]);
    for(int i=1;i<=n;++i) f[i]=c[a[i]-a[1]+1];
    for(int i=2;i<=n;++i)
    for(int j=1;j     f[i]=min(f[i],f[j]+c[a[i]-a[j+1]+1]);
    cout< }

 

 

GOLD Division

Problem 1. CowPhotography

和银组第样。。。

Problem 2.Simplifying the Farm

一道最小生成树题。问你有多少棵最小生成树。

求最小生成树可用Kruskal算法。先将边按权值排序。然后枚举每条边,每次看后面相同的边,若两点不连通,就记下这条边,然后算下个数就行了。

代码如下:

#include
#define ll long long
#define mod 1000000007
using namespace std;

inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x;
}

struct node{
    int u,v,w;
}e[100001];
int p[40001];

inline bool cmp(node a,node b){
    return a.w }


inline int find(int x){
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}

inline bool merge(int x,int y) {
  int x2=find(x);
  int y2=find(y);
  if(x2==y2) return 0;
  p[x2]=p[y2]=p[x]=p[y]=x2;
  return 1;
}

int main()
{
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    int n=read(),m=read();
    for(int i=1;i<=m;++i){
        e[i].u=read(),e[i].v=read(),e[i].w=read();
    }
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;++i) p[i]=i;
    ll s1=0,s2=1;
    for(int i=1;i<=m;){
        int j,tot=0,num=0;
        set > s;
        for(j=i;j<=m&&e[i].w==e[j].w;++j){
            int x=find(e[j].u),y=find(e[j].v);
            if(x!=y){
                if(x                 s.insert(make_pair(x,y));
                ++tot;
            }
        }
        for(;i             num+=merge(e[i].u,e[i].v);
        }
        s1+=num*e[i-1].w;
        if(tot==3) {
            if(num==1||num==2&&s.size()==3) s2=(s2*3)%mod;
            if(num==2&&s.size()==2) s2=(s2*2)%mod;
         }
         if(tot==2&&num==1) s2=(s2*2)%mod;
    }
    cout< }

 

你可能感兴趣的:(日常练习)