予鈴

競プロとか備忘録とか…

ACPC2017-参加記-

ACPCの参加記です。
  • 0日目

ひたすら新幹線で移動してました。

郡山からズに移動する途中の電車は、ひたすら同じ風景が続いてて、やはり大悪魔や植物が競プロしてたり校内にセグ木が生えている大学に移動するのは並大抵のことではないなと思ってたりしてた。

あと宿泊先のホテルが神でした。

  • 1日目

立命セットだったので基本的に椅子に座る仕事をしていました。G問題(原案が自分)の問題がコンテスト終了5分まで0submitで悲しい気分になってた。

G問題が解けた人は教えてほしいです。

懇親会では隣がuwiさんと怒髪さんだったので、オセロ方式でレート上がらないかなぁと思ってたりしてた。

  • 2日目

二度寝しました。

kenkooooさんのご厚意でタクシーに乗せてもらってコンテスト開始までに間に合いました。(ありがとうございました。)

コンテストはチーム名acpc_TMLEで参加してA,C,Fをときました。

Cで無限にバグらせて本当に申し訳なかった。(9WA)

Fはuwiさんに方針を教えてもらった(実装の仕方も教えてもらった)のでAC

実装もバグらせたし、Dも解説を聞くと十分に解くことができる問題だったので、自分の実力不足を感じたコンテストでした。

 

懇親会では隣の席がc7c7さんだったので、話が盛り上がりすぎて…お酒を飲みすぎました。

RCOさん、ありがとうございます。f:id:yebityon:20170925231646j:plain

楽しかったので…セーフ??

なんだかとても汚い話をしていた気がする…

ホテルに帰ってからは流石に反省して問題を解いて寝ました。

  • 3日目

最終日は大悪魔こと、ウクーニャさん(@ukuuku09)さんとロールさん(@rollman054)さんとチーム名gpa_4.0で参加しました。

自分のgpaが一番低かったんでA問題をときました。

残りはD問題を担当したんですが、WAが取れず終了。悲しいね。

コンテスト中にukuさんにアドバイスを貰ったんですが、その通り実装していれば通せた問題を落として申し訳なかったです。

最後の最後にukuさんとろーるさんがpython*1B問題を通してプロ感がすごかった。

emacsvimを使いこなしてたので、自分も勉強したいなぁ〜と思った。

 

  • まとめ

  1. 普段twitterにいる*2競プロerの方々と喋ることができて楽しかった。
  2. オンサイトとは学ぶことが多いなと感じた。
  3. 2日目3日目ともに戦犯だったので強くなりたい。

 

*1:ukuさんが困っていた問題を手伝えなかったのも反省点

*2:ukuさんやうしさんとも今回のコンテストで初めて喋った。

AOJ -2819 Country in Distortion

Country in Distortion | Aizu Online Judge
拡張Dijkstraの問題です。
各頂点に対して、頂点に到着した速度で次の辺へのコストが代わるので、今回は d[vertex][v]として頂点からの距離を持ちます。
ここで注意しないと行けないのが、無向グラフかつコストが0の辺が存在するので、continueの条件式を \leqとしなければ行けません。
具体的には

 if(d[next.first][_v]<=now.first.first+next.second*v)continue;
            d[next.first][_v]=now.first.first+next.second*v;
            que.push(piii(pii(d[next.first][_v],next.first),_v));

ifの条件式の部分です。
今持っているコストで次の頂点へと推移できる。これをcontinueで弾かなければqueに無限にpushされることになります(REになってしまいます)

#include<bits/stdc++.h>
#define int long long
#define rep(i,n) for(int i=0; i<n;i++)
using namespace std;
#define MAX 1e9
int INF=MAX*MAX;

typedef pair<int,int> pii;
typedef pair<pii,long long> piii;
vector<vector<long long>>d(501,vector<long long>(51,INF));
signed main(){
    int n,m;  cin>>n>>m;
    vector<vector<pii>>edge(n,vector<pii>());
    rep(i,m){
        int a,b,c; cin>>a>>b>>c;
        --a,--b;
        edge[a].push_back(pii(b,c));
        edge[b].push_back(pii(a,c));
    }
    int v0,a,b,c; cin>>v0>>a>>b>>c;
    priority_queue<piii,vector<piii>,greater<piii>>que;
    d[0][v0]=0;
    que.push(piii(pii(0,0),v0));
    while(!que.empty()){
        piii now=que.top(); que.pop();
        int v=now.second;
        int _v=(v*a+b)%c;
/*queに積んでる間に最短距離が更新されたかを防ぐifなので<で良い*/
        if(d[now.first.second][v]<now.first.first)continue;
        for(int i=0; i<edge[now.first.second].size();i++){
            pii next=edge[now.first.second][i];
            if(d[next.first][_v]<=now.first.first+next.second*v)continue;
            d[next.first][_v]=now.first.first+next.second*v;
            que.push(piii(pii(d[next.first][_v],next.first),_v));
        }
    }
    int ans=INF;
    rep(i,51)if(d[n-1][i]!=INF){
        vector<vector<long long>>_d(501,vector<long long>(51,INF));
        _d[n-1][i]=d[n-1][i];
        que.push(piii(pii(d[n-1][i],n-1),i));
        while(!que.empty()){
            piii now=que.top(); que.pop();
            int v=now.second;
            int _v=(v*a+b)%c;
            if(_d[now.first.second][v]<now.first.first)continue;
            for(int i=0; i<edge[now.first.second].size();i++){
                pii next=edge[now.first.second][i];
                if(_d[next.first][_v]<=now.first.first+next.second*v)continue;
                _d[next.first][_v]=now.first.first+next.second*v;
                que.push(piii(pii(_d[next.first][_v],next.first),_v));
            }
        }
        rep(i,51)ans=min(ans,_d[0][i]);
    }
    cout<<ans<<endl;
}

AOJ Highway Express Bus

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0212

最短経路を求める問題ですが、道中にc枚割引券を使うことができます。
 d[vertex][cnt]として、拡張Dijkstraをやります。 cntは今持っている割引券の枚数です。
nowに対して、割引券を使う方と使わない方をqueにpushします。
なんだか拡張Dijkstraはプログラミングしてる感があるので好きです。

#include <vector>
#include <iostream>
#include <utility>
#include <algorithm>
#include <string>
#include <deque>
#include <queue>
#include <tuple>
#include <queue>
#include <functional>
#include <cmath>
#include <iomanip>
#include <map>
#include <set>
#include <numeric>
#include <unordered_map>
#include <unordered_set>
#include <complex>
#include <iterator>
#include <array>
#include <memory>
#include <stack>
#define vi vector<int>
#define vvi vector<vector<int> >
#define ll long long int
#define vl vector<ll>
#define vvl vector<vector<ll>>
#define vb vector<bool>
#define vc vector<char>
#define vs vector<string>
#define ld long double
#define INF 1e9
#define EPS 0.0000000001
#define rep(i,n) for(int i=0;i<n;i++)
#define loop(i,s,n) for(int i=s;i<n;i++)
#define all(in) in.begin(), in.end()
template<class T, class S> void cmin(T &a, const S &b) { if (a > b)a = b; }
template<class T, class S> void cmax(T &a, const S &b) { if (a < b)a = b; }
#define MAX 9999999
using namespace std;
typedef pair<int, int> pii;
typedef pair<double,double>pdd;
typedef pair<ll,ll>pll;
int main(){
    int c,n,m,s,e;
    while(cin>>c>>n>>m>>s>>e,c+n+m+e+s){
        --s,--e;
        vector<vector<pii>>edge(n,vector<pii>());
        rep(i,m){
            int a,b,cost; cin>>a>>b>>cost;
            --a,--b;
            edge[a].push_back(pii(b,cost));
            edge[b].push_back(pii(a,cost));
        }
        typedef  pair<pii,int>piii;
        priority_queue<piii,vector<piii>,greater<piii>>que;
        vector<vector<int>>d(n,vector<int>(11,INF));
        que.push(piii(pii(0,c),s));
        d[s][c]=0;
        while(!que.empty()){
            piii now=que.top(); que.pop();
            if(d[now.second][now.first.second]<now.first.first)continue;
            for(int i=0; i<edge[now.second].size();i++){
                pii next=edge[now.second][i];
                if(d[next.first][now.first.second]>d[now.second][now.first.second]+next.second){
                    d[next.first][now.first.second]=d[now.second][now.first.second]+next.second;
                    que.push(piii(pii(d[next.first][now.first.second],now.first.second),next.first));
                }
                if(now.first.second>0){
                    if(d[next.first][now.first.second-1]>d[now.second][now.first.second]+(next.second)/2){
                        d[next.first][now.first.second-1]=now.first.first+(next.second)/2;
                        que.push(piii(pii(d[next.first][now.first.second-1],now.first.second-1),next.first));
                    }
                }
            }
        }
        int ans=INF;
        for(int i=0; i<=c; i++)cmin(ans,d[e][i]);
        cout<<ans<<endl;
    }
}

ICPC参加記

大会前

いつのまにか自分のMacで参加することになってたので、環境整備係してました。

以前のチーム練習で、毎回include書くのはめんどくさいとわかったので、bitsを入れたりg++のバージョンを上げたりしてました。

チームメイトの素数うさぎ(@wk1080id)としゅもんくん(@shumon_84)はEmacsを使っていたので、とりあえずGNU Emacsを入れることに。

自分もEmacsになれるために、慣れないキーバインドを練習した結果、ctlキーの押し過ぎで小指を複雑骨折して2017年度のICPCは幕を閉じました。

来年は小指を丈夫にして挑みたいです。

 

 

 

 

 

 

 

 

 

 

 

 

※もちろんネタです。しかも自分はXcodeで殴りました。

 

個人的な準備としては、実装やるだけ問題をひたすら解いてました。(特にパズル系とシミュレーションやるだけ

 

 

 本番

(他の二人が詳しく書いてるの簡単に書きます。)

  • 始まってすぐに僕がA問題を解くことになってたので、とりあえずAC。模擬国内でWAったのでさっと通って安心しました。
  • この時の順位が34位だったらしくてちょっと嬉しかった。
  • 素数うさぎがBを実装しているときに、Cを読むと制約的に明らかにやるだけシミュレーションだったので、これバグらせたら本当にヤバイなと思いながら紙コーディング→サンプルが合わない。
  • 焦ったらだめだと言い聞かせながら、うさぎと実装を変わりつつデバッグしてなんとかAC。
  • Cが通って安心しました。
  • B問題は意味がわからないので、二人に任せっきりでした。
  • 他の問題はGがdfsかなぁって考えてたけど、いやまさかこんな後半に探索するだけなんて…って考えたりしましたが、他に解けそうな問題がなかったので実装するもTLEして終わりました。
  • 3完。99位。 ギリギリ2桁でした。

 

終わった後

ICPCが終わって肩の力が少し抜けました。

反省点・・・

  • 圧倒的なアルゴリズム力のNASA。勉強と精進が足りない。
  • 後輩に負けた。悲しみ(後輩は98位
  • 模擬国内の本番になって初めて提出方法を知って、チームに迷惑をかけたこと。
  • もう少し早くCを通したかった。

良かった点・・・

  • 実装力がほんの少しついたかなぁ。
  • 案外やるだけの問題が楽しいことに気づいたので、ICPC直前だけじゃなくて頑張りたい。

 

実は初めてのICPCでした。緊張感がすごい大会でした。チーム戦楽しい。

大会が終わった後は部室でお菓子パーティーやりました。

カタンを初めてやったのですが、後輩にボッコボコにやられたので次は勝ちたい。

 

ARC-B ツリーグラフ

B: ツリーグラフ - AtCoder Regular Contest 030 | AtCoder

n頂点からなるツリーグラフが与えられます。
いくつかの頂点にはお宝があり、そのお宝をすべて回収し、最初の頂点に戻ってくるまでの最小コストを求める問題です。

各頂点について考えます。
現在見ている頂点(vertex)を親に持つ頂点を通る必要がある→子孫のうち1つでも宝石を持っている。
という場合です。
宝石が見つかった場合は親にtrueを、そうでない場合はfalseを返すような再帰関数で解くことができます。

こういう問題が好きです()

#include <vector>
#include <iostream>
#include <utility>
#include <algorithm>
#include <string>
#include <deque>
#include <queue>
#include <tuple>
#include <queue>
#include <functional>
#include <cmath>
#include <iomanip>
#include <map>
#include <set>
#include <numeric>
#include <unordered_map>
#include <unordered_set>
#include <complex>
#include <iterator>
#include <array>
#include <memory>
#include <stack>
#define vi vector<int>
#define vvi vector<vector<int> >
#define ll long long int
#define vl vector<ll>
#define vvl vector<vector<ll>>
#define vb vector<bool>
#define vc vector<char>
#define vs vector<string>
#define ld long double
#define INF 1e9
#define EPS 0.0000000001
#define rep(i,n) for(int i=0;i<n;i++)
#define loop(i,s,n) for(int i=s;i<n;i++)
#define all(in) in.begin(), in.end()
template<class T, class S> void cmin(T &a, const S &b) { if (a > b)a = b; }
template<class T, class S> void cmax(T &a, const S &b) { if (a < b)a = b; }
#define MAX 9999999
using namespace std;
class Edge{
public:
    int to,from,cost;
};
vector<int>jem(101,0);
vector<vector<int>>edge(101);
vector<int>cost(101,0);
bool  dfs(int from,int vertex){
    bool var=false;
    if(edge[vertex].size()==1&&jem[vertex]==0)return false;
    rep(i,edge[vertex].size()){
        int next=edge[vertex][i];
        if(next==from)continue;
        if(dfs(vertex,next))var=true;
    }
    if(var||jem[vertex])cost[from]+=cost[vertex]+2;
    else return false;
    return true;
}
 
int main(){
    int  n,x; cin>>n>>x;
    rep(i,n){
        int foo; cin>>foo;
        if(foo)jem[i+1]=1;
    }
    rep(i,n-1){
        int to,from; cin>>to>>from;
        edge[to].push_back(from);
        edge[from].push_back(to);
    }
    rep(i,edge[x].size())dfs(x,edge[x][i]);
    cout<<cost[x]<<endl;
 
}

AOJ -0235 Sergent Rian

問題文:Sergeant Rian | Aizu Online Judge
すべての橋を爆破する最短コストは、最もコストが掛かる島(葉ではなく、1から到達するまでに最もコストがかかる島)を爆破する順番を最後にすれば良い。
サンプルを最短コストになるように鉛筆でなぞると、最もコストが掛かる島に辿り着くまでの橋以外は二度通ることになります。
したがって解法は

すべての橋を爆破するコスト*2-最長のコスト

となります。

#include <vector>
#include <iostream>
#include <utility>
#include <algorithm>
#include <string>
#include <deque>
#include <queue>
#include <tuple>
#include <queue>
#include <functional>
#include <cmath>
#include <iomanip>
#include <map>
#include <set>
#include <numeric>
#include <unordered_map>
#include <unordered_set>
#include <complex>
#include <iterator>
#include <array>
#include <memory>
#include <stack>
#define vi vector<int>
#define vvi vector<vector<int> >
#define ll long long int
#define vl vector<ll>
#define vvl vector<vector<ll>>
#define vb vector<bool>
#define vc vector<char>
#define vs vector<string>
#define ld long double
#define INF 1e9
#define EPS 0.0000000001
#define rep(i,n) for(int i=0;i<n;i++)
#define loop(i,s,n) for(int i=s;i<n;i++)
#define all(in) in.begin(), in.end()
template<class T, class S> void cmin(T &a, const S &b) { if (a > b)a = b; }
template<class T, class S> void cmax(T &a, const S &b) { if (a < b)a = b; }
#define MAX 9999999
using namespace std;
#define int ll
typedef pair<int, int> pii;
typedef pair<double,double>pdd;
class edge{
public:
    int to;
    int cost;
    void Init(){to=0; cost=0;}
    edge(int t, int c):to(t), cost(c){};
    
};
vi d(100,INF);
vector<edge>Edge[100];
void dijkstra(){
    d[1]=0;
    priority_queue<pii,vector<pii>,greater<pii> >que;
    que.push(pii(0,1));
    while(!que.empty()){
        pii now=que.top(); que.pop();
        int to_v=now.second;
        if(d[to_v]<now.first)continue;
        rep(i,Edge[to_v].size()){
            edge e=Edge[to_v][i];
            if(d[e.to]>d[to_v]+e.cost){
                d[e.to]=d[to_v]+e.cost;
                que.push(pii(d[e.to],e.to));
            }
        }
    }
}
signed main(){
    int n;
    while(cin>>n,n){
        vector<pair<pii,int>>data;
        rep(i,100)Edge[i].clear();
        rep(i,n-1){
            int from,to,cost;
            cin>>from>>to>>cost;
            Edge[from].push_back(edge(to,cost));
            Edge[to].push_back(edge(from,cost));
            data.push_back(make_pair(pii(from,to),cost));
        }
        int sum=0;
        rep(i,n+1)d[i]=INF;
        dijkstra();
        int maxi=0;
        for(int i=0; i<data.size();i++){
            if(data[i].first.first==1){
                if(Edge[data[i].first.second].size()==1)continue;
            }else if(data[i].first.second==1){
                if(Edge[data[i].first.first].size()==1)continue;
            }else if(Edge[data[i].first.first].size()==1 || Edge[data[i].first.second].size()==1)
                continue;
            sum+=data[i].second;
        }
        sum*=2;
        for(int i=1; i<=n;i++){
            if(i==1)continue;
            if(Edge[i].size()==1)continue;
            cmax(maxi, d[i]);
        }
        cout<<sum-maxi<<endl;
    }
}

D-Simple Knapsack

コンテストに参加しなかったのですが、想定解ではない方法で解いたので記事にします。

解法は,重さの制約が特殊なので最初の重さを引いて動的計画法をします。

dp[i][j][k] := j個目まで見た時の価値ik個選んだときの最大値
として、ナップサックのdpをやります。
多次元DPを初めてやったのですが、良い練習になったと思います。

#include <vector>
#include <iostream>
#include <utility>
#include <algorithm>
#include <string>
#include <deque>
#include <queue>
#include <tuple>
#include <queue>
#include <functional>
#include <cmath>
#include <iomanip>
#include <map>
#include <set>
#include <numeric>
#include <unordered_map>
#include <unordered_set>
#include <complex>
#include <iterator>
#include <array>
#include <memory>
#include <stack>
#define vi vector<int>
#define vvi vector<vector<int> >
#define ll long long int
#define vl vector<ll>
#define vvl vector<vector<ll>>
#define vb vector<bool>
#define vc vector<char>
#define vs vector<string>
#define ld long double
#define INF 1e9
#define EPS 0.0000000001
#define rep(i,n) for(int i=0;i<n;i++)
#define loop(i,s,n) for(int i=s;i<n;i++)
#define all(in) in.begin(), in.end()
template<class T, class S> void cmin(T &a, const S &b) { if (a > b)a = b; }
template<class T, class S> void cmax(T &a, const S &b) { if (a < b)a = b; }
#define MAX 9999999
using namespace std;
typedef pair<int, int> pii;
typedef pair<double,double>pdd;
typedef pair<ll,ll>pll;
#include<string.h>
ll dp[505][105][405]={0};
int main(){
    ll N,W; cin>>N>>W;
    vl v,w;
    ll lim=0;
    rep(i,N){
        ll value,wegiht;
        cin>>wegiht>>value;
        if(wegiht>W)continue;
        if(!i)lim=wegiht-1;
        dp[wegiht-lim][i][1]=value;
        v.push_back(value);
        w.push_back(wegiht-lim);
    }
    for(int j=0;j<N-1;j++){
        for(int i=0; i<401;i++){
            for(int k=0; k<=N;k++){
                if(dp[i][j][k]==0)continue;
                dp[i][j+1][k]=max(dp[i][j+1][k],dp[i][j][k]);
                if(i+lim*k+w[j+1]+lim>W)continue;
                cmax(dp[i+w[j+1]][j+1][k+1],dp[i][j][k]+v[j+1]);
            }
        }
    }
    ll ans=0;
    rep(i,401)rep(j,N+1)cmax(ans,dp[i][N-1][j]);
    cout<<ans<<endl;
}