2013-2014 Northwestern European Regional Contest (NWERC 2013)

http://codeforces.com/gym/100405/

补题:

A

已知一个图的点数为n,且改图为连通图。告诉了每两个点之间的最短路的距离。让你构造这个图,且这个图的边数为n。

Idea 1:
删边法。时间接近于O(n^3 )。根据题目中给的矩阵构造一个完全图,然后遍历每条边,看是否需这条边是多余的。如果某条边的两点间的最短路可以由其他点到达,那么这条边就是多余的,可删除。因为保证有解,所以最后这张图里面要么有n条边,要么有n-1条边。最后处理一下即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 2050;
int mp[N][N];
bool is[N][N];
int main()
{
        int n;
        bool flag = false;
        while(scanf("%d",&n)!=EOF)
        {
                if(flag) printf("\n");
                flag = true;
                memset(is,true,sizeof(is));
                for(int i=1;i<=n;i++)
                        for(int j=1;j<=n;j++)
                                scanf("%d",&mp[i][j]);
                for(int i=1;i<=n;i++)
                {
                        for(int j=i+1;j<=n;j++)
                        {
                                for(int k=1;k<=n;k++)
                                {
                                        if(k==i || k==j) continue;
                                        if(mp[i][j] == mp[i][k]+mp[k][j])
                                        {
                                                is[i][j] = false;
                                                break;
                                        }
                                }
                        }
                }
                int cnt = 0;
                for(int i=1;i<=n;i++)
                {
                        for(int j=i+1;j<=n;j++)
                        {
                                if( is[i][j]) 
                                {
                                        printf("%d %d %d\n",i,j,mp[i][j]);
                                        cnt ++;
                                }
                        }
                }
                if(cnt==n -1) printf("%d %d %d\n",1,2,mp[1][2]);
        }
        return 0;
}

Idea2:
将所有的边排序然后做一次最小生成树,O(nlogn)。然后最后在这个图上加一条边。先DFS遍历这个图,计算两点的距离,看是否是最短路。如果是不是最短路,增加本应该最短的那条路。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

const int N = 2050;
struct Node
{
        int u,v,x;
        Node(){}
        Node(int a,int b,int c):u(a),v(b),x(c){}
        friend bool operator < (const Node a,const Node b)
        {
                return a.x < b.x;
        }
}edg[N*N];
int gra[N][N], father[N], dep[N],root[N];
vector<int> vet[N];
bool vis[N];
int find(int x)
{
        int y = x;
        while(y!=father[y]) y = father[y];
        while(x!=father[x])
        {
                int px = father[x];
                father[x] = y;
                x = px;
        }
        return y;
}
void dfs(int u,int time,int fa)
{
       dep[u] = time;
       root[u] = fa;
       vis[u] = true;
       for(int i=0; i<vet[u].size();i++) 
       {
                int v = vet[u][i];
                if(vis[v]) continue;
                dfs(v,time+1,u);
       }
}
int LCA(int x,int y)
{
        int tmp = 0;
        while(x != y )
        {
                if(dep[x] > dep[y])
                {
                    int px = x;
                    x = root[x];
                    tmp += gra[px][x];
                }
                else 
                {
                    int py = y;
                    y = root[y];
                    tmp+=gra[py][y];
                }
        }
        return tmp;
}
int main()
{
        int n;
        bool flag = false;
        while(scanf("%d",&n)!=EOF)
        {
                if(flag) printf("\n");
                flag = true;
                int tot = 0;
                for(int i=1;i<=n;i++)
                {
                        father[i] = i;
                        root[i] = i;
                        vet[i].clear();
                        for(int j=1;j<=n;j++)
                        {
                                scanf("%d",&gra[i][j]);
                                if(i>j) edg[tot++] = Node(i,j,gra[i][j]);
                        }
                }
                sort(edg,edg+tot);
                for(int i=0; i<tot;i++)
                {
                        int x = edg[i].u;
                        int y = edg[i].v;
                        int fx = find(x);
                        int fy = find(y);
                        if(fx==fy) continue;
                        father[fx] = fy;
                        vet[x].push_back(y);
                        vet[y].push_back(x);
                        printf("%d %d %d\n",x,y,edg[i].x);
                }
                memset(vis,false,sizeof(vis));     
                dfs(1,0,1);
                int ans = 0x3f3f3f3f, x=1 , y=2;
                for(int i=1;i<=n;i++)
                {
                        for(int j=i+1;j<=n;j++)
                        {
                                int dis =  LCA(i,j);
                                if(dis == gra[i][j]) continue;
                                if( gra[i][j] < ans)  ans = gra[i][j], x = i, y = j;
                        }
                }
                printf("%d %d %d\n",x,y,gra[x][y]);
        }
        return 0;
}

B

给一个无向图,每个节点都有个权值。找出该图的一个最大团,且最大团内保证路径没有交叉。

DFS爆搜即可。因为符合情况的最大团只有三种情况,要么两个点,要么三个点,要么四个点。。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;

const int N = 500;
vector<int> vet[N];
int arr[N],ans,path[N];
bool is[N][N],vis[N];
void dfs(int u,int id,int nans)
{
    ans = max(ans,nans);
    if( id >=4 ) return;
    for(int i=0; i<vet[u].size();i++)
    {
        int v = vet[u][i], j;
        if(vis[v]) continue;
        for(j=1;j<=id;j++) 
        {
            if(!is[v][path[j]]) break;
        }
        if(j<=id) continue;
        vis[v] = true;
        path[id+1] = v;
        dfs(v,id+1,nans+arr[v]); 
        vis[v] = false;
    }
    return;
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;i++) scanf("%d",&arr[i]),vet[i].clear();
        memset(is,false,sizeof(is));
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            vet[u].push_back(v);
            vet[v].push_back(u);
            is[u][v] = is[v][u] = true;
        }
        ans = 0; 
        for(int i=1;i<=n;i++)
        {
            memset(vis,false,sizeof(vis));
            vis[i] = true;
            path[1] = i;
            dfs(i,1,arr[i]);
        }
        printf("%d\n",ans);
    }
    return 0;
}

G

水。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

int gcd(int a,int b)
{
     return b==0?a:gcd(b,a%b);
}

int main()
{
    int a,b,c;
    while (~scanf("%d%d%d",&a,&b,&c)){
        int x=a*b;
        int y=c-b;
        //cout<<x<<" "<<y<<endl;
        int w=gcd(x,y);
        printf("%d/%d\n",x/w,y/w);
    }
    return 0;
}

F

两种日历的表示方法,同时对应着两种计算闰年的方式。问之前日历的日期对应现在日历的日期。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值