zoukankan      html  css  js  c++  java
  • 2017-2018 ACM-ICPC German Collegiate Programming Contest (GCPC 2017)

    Solved


    B、Buildings

    题意:
    (m) 个面,每面有 (n imes n) 个放个,你有 (c) 种颜色去涂,旋转重合算一种方案。问有多少种涂色方案。

    想法:
    (polya) 模版题

    代码:

    ll qfast(ll x,ll y)
    {   
     ll ans=1;
     while(y)
     {
      if(y&1) ans=ans*x%mod;
      x=x*x%mod;
      y>>=1;
     }
     return ans%mod;
    }
    void run()
    {
     ll n=rdll(),m=rdll(),c=rdll();
     ll x=qfast(c,n*n);
     ll ans=0;
     for(ll i=1;i<=m;i++)
     {
      ans+=qfast(x,__gcd(i,m));
      ans%=mod;
     }
     ans*=qfast(m,mod-2);
     printf("%lld
    ",ans%mod);
    }
    signed main()
    { 
    //  int t=rd();
    // while(t--) 
     run();
     return 0;
    }
    

    C、Joyride

    题意:
    带小孩去公园玩,从 (1) 号点开始,公园里每个项目都有游玩时间 (t) 和游玩花费 (w) ,并且游玩不能中断,每个项目之间会有一些路径相通,每条路径需要 (T) 时间通过。并且在时刻 (x),必须让小孩回到 (1) 号点。如果经过一个游玩项目就必须游玩,问是否有可能在 (x)时刻正好在起始点,有可能的话就求出最小花费。

    想法:
    (dist[i][j]) 表示在 (i) 点,时间为 (j) 时的最小花费。然后建图去跑最短路即可。

    代码:

    int x,n,m,s,t,T;
    int tt[MAXN],pp[MAXN];
    struct qnode{
      int v; 
      ll t,c; 
      qnode(int _v=0,ll _t=0,ll _c=0):v(_v),t(_t),c(_c){} 
      bool operator <(const qnode &r)const{ 
        return c>r.c; 
      } 
    };
     
    vector<int>E[MAXN]; 
    bool vis[2005][2005]; 
    ll dist[2005][2005];  //在第i点,时间为j时的最小花费
    void Dijkstra(){ 
      for(int i=1;i<=2000;i++){
          for(int j=1;j<=2000;j++){
              dist[i][j]=INF;
          }
      }
      mem(vis,false);
      priority_queue<qnode>que; 
      while(!que.empty())que.pop(); 
      dist[1][tt[1]]=pp[1]; 
      //vis[1][tt[1]]=1;
      que.push(qnode(1,tt[1],pp[1]));
      qnode tmp; 
      while(!que.empty()){ 
        tmp=que.top();
        que.pop(); 
        int var=tmp.v;
        ll cost=tmp.c;
        ll time=tmp.t;
        //printf("ver: %d  time: %lld  cost: %lld
    ", var, time, cost);
        if(vis[var][time])continue;
        vis[var][time]=1;
        
        if(time+tt[var]<=x&&cost+pp[var]<dist[var][time+tt[var]]){
            //cout<<cost+pp[var]<<endl;
            dist[var][time+tt[var]]=cost+pp[var];
            que.push(qnode(var,time+tt[var],cost+pp[var]));
        }
        for(int i=0;i<E[var].size();i++){
            int to=E[var][i];
            if(time+T+tt[to]<=x&&cost+pp[to]+0ll<dist[to][time+T+tt[to]]){
                dist[to][time+T+tt[to]]=cost+pp[to];
                que.push(qnode(to,time+T+tt[to],cost+pp[to]));
            }
        }
      } 
    }
    int main()
    {
        scanf("%d",&x);
        scanf("%d%d%d",&n,&m,&T);
        for(int i=1;i<=m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            E[a].push_back(b);
            E[b].push_back(a);
        }
        for(int i=1;i<=n;i++){
            scanf("%d%d",&tt[i],&pp[i]);
        }
        Dijkstra();
        if(dist[1][x]==1e18+5||x<tt[1]){
            printf("It is a trap.");
        }else{
            printf("%lld",dist[1][x]);
        }
    	return 0;
    }
    

    D、Pants On Fire

    想法:
    每个人的关系去建图,通过 (Floyd) ,使用传递闭包处理。

    代码:

    map<string,int>mp;
    int mm[404][404];
    int n,m;
    int ans;
    void Floyd()
    {
    	for(int k=1;k<=ans;k++)
    	{
    		for(int i=1;i<=ans;i++)
    		{
    			for(int j=1;j<=ans;j++)
    			{
    				mm[i][j] = min(mm[i][j],mm[i][k]+mm[k][j]);
    			}
    		}
    	}
    }
    int main()
    {
    	cin>>n>>m;
    	ans = 1;
    	mp.clear();
    	for(int i=1;i<=2*n;i++)
    	{
    		for(int j=1;j<=2*n;j++)
    		{
    			if(i!=j) mm[i][j] = INF;
    		}
    	}
    	for(int i=1;i<=n;i++)
    	{
    		string a,b,c,d,e;
    		cin>>a>>b>>c>>e>>d;
    		if(!mp[a])
    		{
    			//cout<<a<<endl;
    			mp[a] = ans;
    			ans++;
    		}
    		if(!mp[d])
    		{
    			//cout<<d<<endl;
    			mp[d] = ans;
    			ans++;
    		}
    		mm[mp[a]][mp[d]] = 1;
    	}
    	Floyd();
    	/*map<string,int>::iterator it;
    	for(it=mp.begin();it!=mp.end();it++)
    	{
    		cout<<it->first<<" "<<it->second<<endl;
    	}*/
    	//cout<<mm[3][1]<<mm[1][2]<<endl;
    	while(m--)
    	{
    		string a,b,c,d,e;
    		cin>>a>>b>>c>>e>>d;
    		if(mp[a]==0||mp[d]==0)
    		{
    			puts("Pants on Fire");
    			continue;
    		}
    		//cout<<mp[a]<<" "<<mp[d]<<endl;
    		//cout<<mm[mp[a]][mp[d]]<<" "<<mm[mp[d]][mp[a]]<<endl;
    		if(mm[mp[a]][mp[d]]==INF&&mm[mp[d]][mp[a]]==INF)
    		{
    			puts("Pants on Fire");
    		}
    		else if(mm[mp[a]][mp[d]]!=INF&&mm[mp[d]][mp[a]]==INF)
    		{
    			puts("Fact");
    		}
    		else{
    			puts("Alternative Fact");
    		}
    	}
    }
    

    E、Perpetuum Mobile

    题意:
    给出一副有向图,问图上是否存在环,其环上的边权乘积大于 (1)

    想法:
    因为边权是浮点数,且需要进行乘法运算,所以我们给每个边权加一个 (log),把乘法运算变成加法运算。最后我们可以通过 (spfa) 来判断是否存在正环,并且此时 (spfa) 中需要求的是最长路。

    代码:

    const int NUM=4005;
    struct edge{
    	int to,next;
      double w;
    	edge(int a,int b,double c){to=a;next=b;w=c;}
    };
    vector<edge>e[NUM];
    int n,m;
    /*
    int pre[NUM];
    void print_path(int s,int t)
    {
       if(s==t)printf("%d",s);
       print_path(s,pre[t]);
       printf("%d",t);
    }
    */
    bool spfa()
    {
      double dis[NUM];
      bool inq[NUM];
      int neg[NUM];
      for(int i=1;i<=n;i++){dis[i]=-1000000;inq[i]=false;neg[i]=0;}
        
    	queue<int>Q;
      for(int i=1;i<=n;i++){
        Q.push(i);
        inq[i]=true;
      }
    	while(!Q.empty())
    	{
    		int u=Q.front();
    		Q.pop();
    		inq[u]=false;
    		for(int i=0;i<e[u].size();i++){
    			int v=e[u][i].next;
          double w=e[u][i].w;
    			if(dis[v]<dis[u]+w){
    				dis[v]=dis[u]+w;
    				//pre[v]=u;
    				if(!inq[v]){
                       Q.push(v);
                       inq[v]=true;
                       neg[v]++;
                       if(neg[v]>=n){return true;}
    				}
    			}
    		}
    	}
    	//printf("%d
    ",dis[n]);
    	return false;
    }
    int main()
    {
    	  scanf("%d%d",&n,&m);
    		for(int i=1;i<=m;i++){
    			int a,b;
          double c;
    			scanf("%d %d %lf",&a,&b,&c);
    			e[a].push_back(edge(a,b,log(c)));
    		}
    		if(!spfa()){
          printf("admissible
    ");
        }else{
          printf("inadmissible
    ");
        }
     
    	return 0;
    }
    

    F、Plug It In!

    题意:
    (n) 中插头, (m) 个电, (k) 种电器和插头匹配方案,你可以选择一种插座将他的数量从1变为3,问最多可以有多少个电器可以匹配插头。

    想法:

    • 考虑没有增加插头时的匹配方案,即一次二分图匹配即可。
    • 增加插头,那么如果枚举增加插头种类再进行二分图匹配,时间复杂度 (O(n^3)) ,会超时。
    • 想到之前已经匹配好的可以作为重新匹配的图,每次重新匹配在原来已经匹配好的图上加入两个新点,那么每次重新匹配时只需要匹配两个点即可。

    代码:

    int G[MAXN][MAXN];
    int match[MAXN],reserve_boy[MAXN],match2[MAXN];
    int k,m,n;
     
    bool dfs(int x)
    {
         for(int i=1;i<=n;i++){
             if(!reserve_boy[i]&&G[x][i]){
                 reserve_boy[i]=1;
                 if(!match[i]||dfs(match[i])){
                     match[i]=x;
                     return true;
                 }
             }
         }
         return false;
    }
    bool dfs2(int x)
    {
         for(int i=1;i<=n;i++){
             if(!reserve_boy[i]&&G[x][i]){
                 reserve_boy[i]=1;
                 if(!match2[i]||dfs2(match2[i])){
                     match2[i]=x;
                     return true;
                 }
             }
         }
         return false;
    }
    vector<int>g[MAXN];
    int main()
    {
        scanf("%d%d%d",&m,&n,&k);
        memset(G,0,sizeof G);
        memset(match,0,sizeof match);
        for(int i=0;i<k;i++){
            int a,b;
            scanf("%d %d",&a,&b);
            g[a].push_back(b);
            G[a][b]=1;
        }
        int sum=0;
        for(int i=1;i<=m;i++){
            memset(reserve_boy,0,sizeof reserve_boy);
            if(dfs(i))sum++;
        }
        /*
        for(int i=1;i<=n;i++){
            printf("%d
    ",match[i]);
        }
        */
        int maxx=sum,ans;
        for(int i=1;i<=m;i++){
            ans=0;
            mem(match2,0);
            for(int j=1;j<=n;j++){
                match2[j]=match[j];
            }
            for(int j=0;j<g[i].size();j++){
                G[m+1][g[i][j]]=1;
                G[m+2][g[i][j]]=1;
            }
            ans=sum;
            for(int j=m+1;j<=m+2;j++){
                memset(reserve_boy,0,sizeof reserve_boy);
                if(dfs2(j))ans++;
            }
            maxx=max(maxx,ans);
            for(int j=0;j<g[i].size();j++){
                G[m+1][g[i][j]]=0;
                G[m+2][g[i][j]]=0;
            }
        }
        printf("%d
    ",maxx);
        return 0;
    }
    

    G、Water Testing

    题意:
    按顺序给出 (n) 个点,问由这 (n) 个点构成的多边形内部有多少个整数点。

    想法:

    • (pick) 定理(S = n+frac{x}{2}-1),其中 (S)=多边形面积,(n)=多边形内部整数点(,)x$=多边形的边上的整数点。

    • 多边形面积:(S = frac{1}{2}abs( x_{1} imes y_{2}-x_{2} imes y_{1}+......+x_{n-1} imes y_{n}-x_{n} imes y_{n-1}+x_{n} imes y_{1}-x_{1} imes y_{n})

    • 边上整数点:(x = gcd(x_{1}-x_{2},y_{1}-y_{2})+......+gcd(x_{n-1}-x_{n},y_{n-1}-y_{n})+gcd(x_{n}-x_{1},y_{n}-y_{1}))

    代码:

    ll gcd(ll a,ll b)
    {
        if(b == 0)
            return a;
        return gcd(b,a%b);
    }
     
    ll a[maxn],b[maxn];
    int n;
    int main()
    {
        int n;
        cin >> n;
        ll x0,y0,x,y,S = 0;
        cin >> x >> y;
        x0 = x;
        y0 = y;
        a[1] = x;
        b[1] = y;
        for(int i=2;i<=n;i++) {
        	ll xtmp,ytmp;
            cin >> xtmp >> ytmp;
            a[i] = xtmp;
            b[i] = ytmp;
            S += (x*ytmp-y*xtmp);
            x = xtmp;
            y = ytmp;
        }
        S += (x*y0-y*x0);
        ll sum = 0;
       // cout<<S<<endl;
        for(int i=1;i<=n;i++){
           if(i<n){
            ll aa=abs(a[i+1]-a[i]);
            ll bb=abs(b[i+1]-b[i]);
            sum+=gcd(aa,bb)-1;
           }else{
            ll aa=abs(a[1]-a[n]);
            ll bb=abs(b[1]-b[n]);
            sum+=gcd(aa,bb)-1;
           }
        }
        sum+=n;
        printf("%lld
    ",abs(S)/2+1-sum/2);
    }
    

    I、Überwatch

    想法:
    简单dp,转移方程为:

    $dp[i]=dp[i-1]$ (不选)
    $dp[i]=max(dp[i],dp[i-m]+a[i])$ (选了)

    代码:

    int dp[maxn];
    int a[maxn];
    int main()
    {
      int n,m;
      cin>>n>>m;
      for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
      }
      int ans=0;
      for(int i=m+1;i<=n;i++){
        dp[i]=dp[i-1];
        dp[i]=max(dp[i-m]+a[i],dp[i]);
      }
      cout<<dp[n];
    }
    

    K、You Are Fired!

    water

    代码:

    struct node{
        string s;
        ll num;
    }p[10005];
    bool cmp(node a,node b){
      return a.num>b.num;
    }
    vector<string>pp;
    int main()
    {
        ll n,d,k;
        pp.clear();
        scanf("%lld%lld%lld",&n,&d,&k);
        for(int i=1;i<=n;i++){
           cin>>p[i].s;
           scanf("%lld",&p[i].num);
        }
        sort(p+1,p+n+1,cmp);
        ll sum=0;
        for(int i=1;i<=k;i++){
          sum+=p[i].num;
          pp.push_back(p[i].s);
          if(sum>=d)break;
        }
        if(sum<d){
          printf("impossible");
        }else{
          cout<<pp.size()<<endl;
          for(int i=0;i<pp.size();i++){
            cout<<pp[i]<<", YOU ARE FIRED!"<<endl;
          }
        }
    }
    
    越自律,越自由
  • 相关阅读:
    KingPaper初探Java之初学者编码遇到的问题
    KingPaper初探redis之redis数据类型解析(String类型)
    KingPaper初探Java之面向对象对象的声名和实例化(一)
    KingPaper初探百度应用之百度地图API
    MYSQL之用户授权
    nginx入门到精通目录
    nginx入门篇负载均衡策略
    nginx入门篇功能特性
    开博啦
    ubuntu14.04下pycharm的安装及破解
  • 原文地址:https://www.cnblogs.com/ha-chuochuo/p/14353633.html
Copyright © 2011-2022 走看看