NWERC 2012 练习赛题解

68 篇文章 0 订阅
29 篇文章 0 订阅

                题目:

                         Click here

A - Admiral

                题意:

                          有V(2<=V<=1000个点编号为1~V..有E(3<=E<=10000)条有向边...现在两个人同时从1点出发..要到达V点..除了起点和终点..中间不能走相同的边..相同的点..问两人路径总和最少是多少...

                题解:

                          陈题...拆点..起点和终点做边容量为2..费用为0(因为起点和终点会经过两次)...其他点间做容量为1..费用为0的有向边...然后点之间的边容量为1..费用为其长度..然后跑从1到 V点跑一次最小费用最大流..得到的最小费用就是答案..


Program:

#include<iostream>  
#include<algorithm>  
#include<stdio.h>  
#include<string.h>  
#include<math.h>  
#include<queue>  
#define MAXN 2205
#define MAXM 50005  
#define oo 10000007  
#define ll long long  
using namespace std;    
struct MCMF      
{      
       struct node      
       {      
              int x,y,c,v,next;       
       }line[MAXM];      
       int Lnum,_next[MAXN],pre[MAXN],dis[MAXN],flow,cost;      
       bool inqueue[MAXN];      
       void initial(int n)      
       {      
              Lnum=-1;      
              for (int i=0;i<=n;i++) _next[i]=-1;      
       }      
       void addline(int x,int y,int c,int v)      
       {      
              line[++Lnum].next=_next[x],_next[x]=Lnum;      
              line[Lnum].x=x,line[Lnum].y=y,line[Lnum].c=c,line[Lnum].v=v;      
              line[++Lnum].next=_next[y],_next[y]=Lnum;      
              line[Lnum].x=y,line[Lnum].y=x,line[Lnum].c=0,line[Lnum].v=-v;      
       }      
       bool SPFA(int s,int e)      
       {      
              int x,k,y;      
              queue<int> Q;      
              while (!Q.empty()) Q.pop();      
              memset(dis,0x7f,sizeof(dis));      
              memset(inqueue,false,sizeof(inqueue));      
              Q.push(s);      
              dis[s]=0,pre[s]=-1;      
              while (!Q.empty())      
              {      
                      x=Q.front(),Q.pop(),inqueue[x]=false;      
                      for (k=_next[x];k!=-1;k=line[k].next)        
                         if (line[k].c)      
                         {      
                               y=line[k].y;      
                               if (dis[y]>dis[x]+line[k].v)      
                               {      
                                        dis[y]=dis[x]+line[k].v;      
                                        pre[y]=k;      
                                        if (!inqueue[y])      
                                        {      
                                                inqueue[y]=true;      
                                                Q.push(y);      
                                        }      
                               }      
                         }      
              }      
              if (dis[e]>oo) return false;      
              flow=oo,cost=0;      
              for (k=pre[e];k!=-1;k=pre[line[k].x])       
                  flow=min(flow,line[k].c),cost+=line[k].v;          
              cost*=flow;      
              for (k=pre[e];k!=-1;k=pre[line[k].x])      
                  line[k].c-=flow,line[k^1].c+=flow;        
              return true;      
       }      
       void MinCostMaxFlow(int s,int e,int &Aflow,int &Acost)      
       {      
              Aflow=0,Acost=0;      
              while (SPFA(s,e))      
              {      
                     Aflow+=flow;      
                     Acost+=cost;       
              }           
       }      
}T;   
int main() 
{         
      int V,E,s,e,Af,Ac,u,v,c,i;     
      while (~scanf("%d%d",&V,&E))
      { 
              s=1<<1,e=V<<1|1,T.initial(e+10); 
              for (i=2;i<V;i++) T.addline(i<<1,i<<1|1,1,0);
              T.addline(1<<1,1<<1|1,2,0),T.addline(V<<1,V<<1|1,2,0);
              while (E--)
              {
                      scanf("%d%d%d",&u,&v,&c);
                      T.addline(u<<1|1,v<<1,1,c);
              }
              T.MinCostMaxFlow(s,e,Af,Ac);
              printf("%d\n",Ac);
      } 
      return 0;
}
/*
5 6
1 5 100
1 2 1
1 3 1
2 4 1
3 4 1
4 5 1
 
6 11
1 2 23
1 3 12
1 4 99
2 5 17
2 6 73
3 5 3
3 6 21
4 6 8
5 2 33
5 4 5
6 5 20
*/


D - Digital Clock

                题意:

                          现在一个每个数字7数码的数码钟可能显示有问题了...有问题是指可能有些位置不亮了..现在给出 N(1<=N<=50)个分钟连续递增的世界..可能存在显示错误..能推断出第一个时间可能是那些吗?...

                题解:

                          阅读理解题...打出每个数字的7个数码位的01情况..然后24*60的暴力枚举第一个时间..然后推过去判断..值得注意的是一个钟的数位好坏在不同时间上是一样的..所以要更新那些数位确定有问题..哪些数位确定是好的..哪些数位是还未知的..


Program:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <limits>
#include <cstdlib> 
using namespace std; 
struct node
{
      int h0,h1,m0,m1;
}P[55];
/*bool C[10][10]={{1,1,0,0,0,0,0,1,0,0},
                {0,1,0,0,0,0,0,0,0,0},
                {0,0,1,0,0,0,0,0,0,0},
                {0,1,0,1,0,0,0,1,0,0},
                {0,1,0,0,1,0,0,0,0,0},
                {0,0,0,0,0,1,0,0,0,0},
                {0,0,0,0,0,1,1,0,0,0},
                {0,1,0,0,0,0,0,1,0,0},
                {1,1,1,1,1,1,1,1,1,1},
                {0,1,0,1,1,1,0,1,0,1} 
               };    */
bool C[10][7]={{1,1,1,0,1,1,1},
               {0,0,1,0,0,1,0},
               {1,0,1,1,1,0,1},
               {1,0,1,1,0,1,1},
               {0,1,1,1,0,1,0},
               {1,1,0,1,0,1,1},
               {1,1,0,1,1,1,1},
               {1,0,1,0,0,1,0},
               {1,1,1,1,1,1,1},
               {1,1,1,1,0,1,1}            
              };
int H[4][7];
bool ok(int p,int x0,int x)
{ 
      for (int i=0;i<7;i++) 
      {
             if (!C[x0][i] && C[x][i]) return false;
             if (H[p][i]==0 && C[x][i]) return false;
             if (H[p][i]==1 && C[x0][i] && !C[x][i]) return false;
             if (C[x0][i] && C[x][i]) H[p][i]=1;
             if (C[x0][i] && !C[x][i]) H[p][i]=0;
      } 
      return true;
}
bool judge(int n,int h,int m)
{
      int t,h0,h1,m0,m1;  
      for (t=0;t<4;t++)
         for (h0=0;h0<7;h0++)
           H[t][h0]=-1;  
     // h=23,m=25;//
      for (t=1;t<=n;t++)
      {
             h0=h/10,h1=h%10;
             m0=m/10,m1=m%10; 
             if (!ok(0,h0,P[t].h0)) return false;
             if (!ok(1,h1,P[t].h1)) return false;
             if (!ok(2,m0,P[t].m0)) return false;
             if (!ok(3,m1,P[t].m1)) return false;
             m++;
             if (m==60) m=0,h++;
             if (h==24) h=m=0;
      }
      return true;
} 
int main()
{ 
      int n,i,h,m; 
      bool f; 
      char c; 
     // freopen("input.txt","r",stdin);      freopen("output.txt","w",stdout);  
      while (~scanf("%d",&n))
      {
              for (i=1;i<=n;i++)  
              { 
                    scanf("%d",&h);
                    do { c=getchar(); } while (c!=':');
                    scanf("%d",&m);
                    P[i].h0=h/10,P[i].h1=h%10;
                    P[i].m0=m/10,P[i].m1=m%10;
              } 
              f=false;
              for (h=0;h<24;h++)
                 for (m=0;m<60;m++)
                    if (judge(n,h,m)) 
                    {
                             if (f) printf(" ");
                             printf("%d%d:%d%d",h/10,h%10,m/10,m%10);                                       
                             f=true;
                    } 
              if (!f) printf("none");
              printf("\n");
      }
      return 0;
}


E - Edge Case

                题意:

                          没读这题.....

                题解:

                          从3开始的Fibonacci数列...但是结果会很大.. 要用大数

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <limits>
#include <cstdlib> 
using namespace std;
 
const int MAXD = 10000, DIG = 9, BASE = 1000000000;
 
const unsigned long long BOUND = numeric_limits <unsigned long long> :: max () - (unsigned long long) BASE * BASE;
 
class bignum
{
private:
    int digits[MAXD];
    int D;
public:
 
    friend ostream &operator<<(ostream &out,bignum &c); 
    inline void trim()
    {
        while(D > 1 && digits[D-1] == 0 )
            D--;
    }
 
    inline void dealint(long long x)
    {
        memset(digits,0,sizeof(digits));
        D = 0;
        do
        {
            digits[D++] = x % BASE;
            x /= BASE;
        }
        while(x > 0);
    }
 
    inline void dealstr(char *s)
    {
        memset(digits,0,sizeof(digits));
        int len = strlen(s),first = (len + DIG -1)%DIG + 1; 
        D = (len+DIG-1)/DIG; 
        for(int i = 0; i < first; i++)
            digits[D-1] = digits[D-1]*10 + s[i] - '0'; 
        for(int i = first, d = D-2; i < len; i+=DIG,d--)
            for(int j = i; j < i+DIG; j++)
                digits[d] = digits[d]*10 + s[j]-'0'; 
        trim();
    }
 
    inline char *print()
    {
        trim(); 
        char *cdigits = new char[DIG * D + 1]; 
        int pos = 0,d = digits[D-1]; 
        do
        {
            cdigits[pos++] = d % 10 + '0';
            d/=10;
        }
        while(d > 0); 
        reverse(cdigits,cdigits+pos); 
        for(int i = D - 2; i >= 0; i--,pos += DIG)
            for(int j = DIG-1,t = digits[i]; j >= 0; j--)
            {
                cdigits[pos+j] = t%10 + '0';
                t /= 10;
            } 
        cdigits[pos] = '\0'; 
        return cdigits;
    } 
 
    bignum()
    {
        dealint(0);
    }
 
    bignum(long long x)
    {
        dealint(x);
    }
 
    bignum(int x)
    {
        dealint(x);
    }
 
    bignum(char *s)
    {
        dealstr(s);
    }
   
    inline bignum operator + (const bignum &o) const
    {
        bignum sum = o;
        int carry = 0; 
        for (sum.D = 0; sum.D < D || carry > 0; sum.D++)
        {
            sum.digits [sum.D] += (sum.D < D ? digits [sum.D] : 0) + carry; 
            if (sum.digits [sum.D] >= BASE)
            {
                sum.digits [sum.D] -= BASE;
                carry = 1;
            }
            else
                carry = 0;
        } 
        sum.D = max (sum.D, o.D);
        sum.trim ();
        return sum;
    } 
};
 
ostream &operator<<(ostream &out, bignum &c)
{
    out<<c.print();
    return out;
} 
int main()
{
    int p;
    bignum a,b,c;   
    while (cin>>p) 
    {
          if (p==3) { printf("4\n");  continue; } 
          p-=4,a=4,b=7;
          while (p--)
          {
                 c=a+b;
                 a=b,b=c;
          }
         cout<<b<<endl;
    }
}


D - Idol

                题意:

                          现在进行偶像选秀节目..有N(1<=N<=1000)个选手..编号为1~N...有M(1<=M<=10000)个评委..每个评委会投出两票..若一个票x为正数则说明该评委希望x晋级..若一个票为负数..则说明该评委希望-x被淘汰..现在1号选手黑入了系统..看到了每个评委的票..于是决定安排一个晋级方案..包括自己..同时让所有的评委两票中至少有一个满足条件..请问是否存在晋级方案...

                题解:

                          阅读理解...读懂题后就发现是简单的2-sat..因为对于一个评委...如果一个人不满足了..另一个必须满足..可以构造关系..然后从1号晋级的点出发dfs染色..判断若其染色的点有一个人淘汰而又晋级的就说明无解..输出no...否则输出yes...


Program:

#include<iostream>  
#include<algorithm>  
#include<stdio.h>  
#include<string.h>  
#include<math.h>  
#include<stack>
#include<queue>  
#define MAXN 2205
#define MAXM 25005  
#define oo 1000000007  
#define ll long long  
using namespace std;      
struct node
{
      int u,v,next; 
}edge[MAXM];
int ne,_next[MAXN],DfsIndex,tpnum,tp[MAXN],dfn[MAXN],low[MAXN]; 
void addedge(int u,int v)
{
      edge[++ne].next=_next[u],_next[u]=ne;
      edge[ne].u=u,edge[ne].v=v;
} 
bool instack[MAXN];
stack<int> S;
void tarjan(int x)
{
      dfn[x]=low[x]=++DfsIndex;
      instack[x]=true,S.push(x);
      for (int k=_next[x];k;k=edge[k].next)
      {
              int v=edge[k].v;
              if (!dfn[v])
              {
                     tarjan(v);
                     low[x]=min(low[x],low[v]);
              }else
              if (instack[v])
                     low[x]=min(low[x],dfn[v]);                           
      }
      if (dfn[x]==low[x])
      {
              tpnum++;
              do
              {
                     x=S.top(),S.pop();
                     instack[x]=false;
                     tp[x]=tpnum;
              }while (low[x]!=dfn[x]);
      }
}
bool color[MAXN];
bool judge(int n)
{
      for (int i=1;i<=n;i++)
      {
           if (tp[i<<1]==tp[i<<1|1]) return false;
           if (color[i<<1] && color[i<<1|1]) return false;
      }      
      return true;
} 
void dfs(int x)
{
      color[x]=1;
      for (int k=_next[x];k;k=edge[k].next)
         if (!color[edge[k].v]) dfs(edge[k].v);
}
int main() 
{          
      int n,m,u,v,i; 
      while (~scanf("%d%d",&n,&m))
      {
              ne=0,memset(_next,0,sizeof(_next));
              while (m--)
              {
                      scanf("%d%d",&u,&v);
                      if (u<0) u=(-u)<<1;
                          else u=u<<1|1;
                      if (v<0) v=(-v)<<1;
                          else v=v<<1|1; 
                      addedge(u^1,v),addedge(v^1,u);
              }
              memset(dfn,0,sizeof(dfn));
              memset(instack,false,sizeof(instack));
              while (!S.empty()) S.pop();
              tpnum=DfsIndex=0;
              for (i=2;i<=(n<<1|1);i++)
                 if (!dfn[i]) tarjan(i); 
              memset(color,false,sizeof(color));
              dfs(3);
              if (!judge(n)) printf("no\n");
                        else printf("yes\n");                        
      }
      return 0;
}

J - Joint Venture

                题意:

                          现在给出一个数(1是特殊的..代表10000000)..又给出N(2<=N<=500000)个数...问能否从这N个数中选择两个数A,B相加等于给的第一个数..若能..请输出[A-B]最大的..

                题解:

                          先排序..然后扫描上界..二分出下届..更新答案..

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
 
int x,a[1000010];
int n;
 
int main()
{ 
    while(scanf("%d",&x)!=EOF){
         x*=(int)1e7;
         scanf("%d",&n);
         for(int i=1;i<=n;i++) scanf("%d",&a[i]);  
         sort(a+1,a+1+n);
         bool found=false;
         for(int i=1;i<=n;i++)
         {
                 int  tt=x-a[i];
                 int High=n,Low=i+1,mid;
                 while(Low<High){
                     mid=Low+(High-Low)/2;
                     if(a[mid]>=tt)   High=mid;
                     else  Low=mid+1;
                 }
                 if(a[Low]==tt && Low<=n )
                 {
                       printf("yes %d %d\n",a[i],a[Low]);
                       found=true;
                       break;              
                 }
                 if(a[i]>x) break;
         }            
          if(!found) printf("danger\n" );           
    }    
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值