2015-2016 下半學期 第一週 訓練

1、UVA1423

題意:給出一個矩陣表示一個序列任意區間內區間和的正負,構造一個序列滿足這個矩陣。

題解:首先看到給了區間和,第一個想法就應該是轉化到前綴和的差,轉化之後發現序列具有一種顯見的偏序關係,然後這種偏序關係輕易地就想到了轉換爲圖上問題去解決,這種思想在差分約束中也比較常見,以後有時間詳細總結。然後考慮到了topo序,如果我們每次要求topo序爲i的前綴和與比它topo序大的前綴和中最小的前綴和的差距不大於1,最大的前綴和數爲10,最後求出來的數就一定在[-10,10]裏面。我們把pre[i]向比它小的pre[j]建有向邊,然後topo序在後的肯定不大於topo序在前的,這是顯然的, 然後假設我們已經把topo序前k大的求出來了,求到第k+1大的時候,把它設成pre[k]-1並不會導致無解,如果把最大的pre設成10,找到一個topo序更靠後的,把它的值設成9,下一個設成8……就是這種感覺。我們就可以把topo序第k大的pre設成第k-1大的pre值-1,因爲是topo排序,不會影響到pre之間的大小關係。

代碼

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long  long
#define db double
#define EPS 1e-15
#define inf 1e10

using namespace std;

int T,n;
vector<int> G[15];
int deg[15];
char S[305];
int ans[15];
void add(int from,int to){
    G[from].push_back(to);
    deg[to]++;
}
void init(){
    memset(deg,0,sizeof(deg));
    scanf("%d\n",&n);
    for (int i=0;i<=n;i++)
        G[i].clear();
    int tot=0;
    scanf("%s\n",S+1);
    for (int i=1;i<=n;i++)
        for (int j=i;j<=n;j++)
            if (S[++tot]=='+')
                add(j,i-1);
            else if (S[tot]=='-')
                add(i-1,j);
}
void Sort(){
    queue<int> Q;
    for(int i=0;i<=n;i++)
        if(deg[i]==0)
            Q.push(i);
    int now=10;
    while(!Q.empty()){
        int u=Q.front();Q.pop();
        ans[u]=now;
        for(int i=0;i<G[u].size();i++){
            int v=G[u][i];
            deg[v]--;
            if(deg[v]==0){
                now--;
                Q.push(v);
            }
        }
    }
}
int main(){
    scanf("%d",&T);
    for (int i=1;i<=T;i++){
        init();
        Sort();
        for (int i=1;i<n;i++)
            printf("%d ",ans[i]-ans[i-1]);
        printf("%d\n",ans[n]-ans[n-1]);
    }
    return 0;
}

2、hdu3018

題意:問畫出一個圖最少需要多少筆。

題解:歐拉回路模板題,先利用並查集統計出圖裏有多少個連通分支,然後統計一下各個連通分支里歐拉回路或者歐拉道路的個數。

代碼

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long  long
#define db double
#define EPS 1e-15
#define inf 1e10

using namespace std;

const int maxn = 100010;
int f[maxn],d[maxn],odd[maxn];
bool vis[maxn];
int find(int x){
    if(x!=f[x]) f[x] = find(f[x]);
    return f[x];
}
vector<int>p;
int main(){
    int n,m,u,v;
    while(~scanf("%d%d",&n,&m)){
        for(int i=0;i<maxn;i++){
            f[i]=i;
            odd[i]=d[i]=0;
            vis[i]=0;
        }
        p.clear();
        for(int i=1;i<=m;i++){
            scanf("%d%d",&u,&v);
            d[u]++; d[v]++;
            u=find(u); v=find(v);
            if(u!=v) f[v]=u;
        }
        for(int i=1;i<=n;++i){
            u=find(i);
            if(!vis[u]){
                vis[u]=1;
                p.push_back(u);
            }
            if(d[i]&1) odd[u]++;
        }
        int ret=0;
        for(int i=0;i<p.size();i++){
            if(!d[p[i]]) continue;
            if(odd[p[i]]==0) ret++;
            else ret+=odd[p[i]]/2;
        }
        printf("%d\n",ret);
    }
    return 0;
}

3、poj2439

題意:2*n個孩子,每個孩子最多有n-1個仇人,有仇的兩個孩子不能相鄰。求安排座位。

題解:哈密頓迴路模板,根據初中數學可以看出來必定是有解的。然後套模板求個可行遍歷就可以了。關於如何求哈密頓迴路之前的博客裏講過。

代碼

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long  long
#define db double
#define EPS 1e-15
#define inf 1e10

using namespace std;

const int maxn=420;
int n,m;
bool mp[maxn][maxn];
int S,T,cnt,ans[maxn];
bool vis[maxn];

void reverse(int l,int r){
    while (l<r){
        swap(ans[l],ans[r]);
        l++,r--;
    }
}
void expend(){
    for (;;){
        bool flag=0;
        for (int i=1;i<=n;i++){
            if (!vis[i] && mp[T][i]){
                ans[cnt++]=i;
                T=i;
                vis[i]=1;
                flag=1;
                break;
            }
        }
        if (!flag) break;
    }
}
void hamiltun(){
    memset(vis,0,sizeof(vis));

    S=1;
    for (T=2;T<=n;T++) if (mp[S][T]) break; 
    cnt=2;
    ans[0]=S, ans[1]=T;
    vis[S]=1, vis[T]=1;
    while (1) {
        expend();  
        reverse(0,cnt-1);
        swap(S,T);
        expend();
        int mid=0;
        if (!mp[S][T]){
            for (int i=1;i<cnt-2;i++){
                if (mp[ans[i]][T] && mp[ans[i+1]][S]){
                    mid=i+1;
                    break;
                }
            }
            reverse(mid,cnt-1);
            T=ans[cnt-1];
        }
        if (cnt==n) break;
        for (int i=1;i<=n;i++){
            if (!vis[i]){
                int j;
                for (j=1;j<cnt-1;j++)
                    if (mp[ans[j]][i]) break;
                if (mp[ans[j]][i]){
                    T=i; mid=j;
                    break;
                }
            }
        }
        S=ans[mid-1];
        reverse(0,mid-1);
        reverse(mid,cnt-1);
        ans[cnt++]=T;
        vis[T]=1;
    }
}
int main(){
    int u,v;
    while (~scanf("%d%d",&n,&m)){
        if (n==0 && m==0) break;
        n<<=1;
        memset(mp,1,sizeof(mp));
        for (int i=1;i<=n;i++) mp[i][i]=0;
        for (int i=1;i<=m;i++){
            scanf("%d%d",&u,&v);
            mp[u][v]=0;
            mp[v][u]=0;
        }
        hamiltun();
        printf("%d",ans[0]);
        for (int i=1;i<cnt;i++)
            printf(" %d",ans[i]);
        puts("");
    }   
    return 0;
}

4、hdu5040

題意:……題意好長懶得打了……題號都有啦網上去找嘛!

題解:這個題型我是見得多啦,看上去就是個搜嘛,但是實現起來真是噁心,也可能是我好久沒寫搜索了。首先先不管其他條件搜一下能不能到達終點。如果可以的話,題目要求的這個監控的條件,使得這個題要用到優先隊列,因爲每個格子面對每個方向都有三個選擇,用時都各不相同,所以就把這些情況對應的不同時間都記錄在結構體裏然後丟進優先隊列。第二個難點是如何記錄T秒時監控的方向?結構體裏多維護一個time用來%4就可以啦。

代碼

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long  long
#define db double
#define EPS 1e-15
#define inf 1e10

using namespace std;

const int Max=505;
char str[Max][Max];
char cam[5]="NESW";
int dir[5][2]={-1,0,1,0,0,-1,0,1,0,0};
struct p{
    int pos,time;
    bool operator < (const p& a)const{
        return time>a.time;
    }
    bool operator == (const p& a) const{
        return pos==a.pos;
    }
};
p s,e,tmpp,np;
int n,t,ans,c,nxt;
bool vis[4][Max*Max];
bool fnd(int cur,int i,int& nxt)
{
    int cx=cur/n;
    int cy=cur%n;
    int nx=cx+dir[i][0];
    int ny=cy+dir[i][1];
    if (nx>=0 && nx<n && ny>=0 && ny<n && str[nx][ny]!='#'){
        nxt=nx*n+ny;
        return 1;
    }
    return 0;
}
bool can(){
    bool flag[255025]={0};
    queue<int>q;
    q.push(s.pos);
    while (q.empty()!=1){
        int tmp=q.front();
        q.pop();
        for (int i=0;i<4;i++)
            if (fnd(tmp,i,nxt)==1 && flag[nxt]==0){
                flag[nxt]=1;
                if (nxt==e.pos) return 1;
                q.push(nxt);
            }

    }
    return 0;
}
int isc(int xx,int yy){
    for (int i=0;i<4;i++)
        if (str[xx][yy]==cam[i])
            return i;
    return -1;
}
bool hasc(int pos,int time){
    int x=pos/n;
    int y=pos%n;
    int d;
    if (x-1>=0)
        if ((d=isc(x-1,y))!=-1 && cam[(d+time%4)%4]=='S')
            return 1;
    if (x+1<n)
        if ((d=isc(x+1,y))!=-1 && cam[(d+time%4)%4]=='N')
            return 1;
    if (y-1>=0)
        if ((d=isc(x,y-1))!=-1 && cam[(d+time%4)%4]=='E')
            return 1;
    if (y+1<n)
        if ((d=isc(x,y+1))!=-1 && cam[(d+time%4)%4]=='W')
            return 1;
    if (isc(x,y)!=-1)
        return 1;
    return 0;
}
int main(){
    int T;
    scanf("%d",&T);
    for (int cas=1;cas<=T;cas++){
        scanf("%d",&n);getchar();
        for (int i=0;i<n;i++){
            for (int j=0;j<n;j++){
                cin>>str[i][j];
                if (str[i][j]=='M'){
                    s.pos=i*n+j; s.time=0;
                }
                if (str[i][j]=='T'){
                    e.pos=i*n+j;
                }
            }
        }
        if (can()){
            memset(vis,0,sizeof(vis));
            priority_queue<p> mq;
            mq.push(s);
            vis[s.time%4][s.pos]=1;
            while (1){
                tmpp=mq.top();
                mq.pop();
                if (tmpp.pos==e.pos){
                    ans=tmpp.time;
                    break;
                }
                for (int i=0;i<5;i++)
                if (fnd(tmpp.pos,i,np.pos)==true){
                    if (i<4 && (hasc(tmpp.pos,tmpp.time) || hasc(np.pos,tmpp.time))){
                            np.time=tmpp.time+3;
                        }
                        else np.time=tmpp.time+1;
                        if (vis[np.time%4][np.pos]==0){
                            vis[np.time%4][np.pos]=1;
                            mq.push(np);
                        }
                    }
            }
        }
        else ans=-1;
        printf("Case #%d: %d\n",cas,ans);
    }
    return 0;
}

5、Codeforces 405E

題意:給出一個無向圖問你能不能把圖裏原來邊長爲1的邊接一接變成邊長爲2連接三個點的邊並覆蓋全圖。

題解:這個題看錯題了,看了好久都沒理解題意。以爲是把原來的邊剖開然後找點接上,怎麼想都想不明白。其實就是對點進行dfs,找到一個點發現這個點有落單邊,則把這個落單邊連的終點和搜的這個點和上條邊連的點輸出,有時候這個點是一個類似於中心的點,這樣就把其他連着它的點都丟進這個點專屬的隊列裏,然後輸出就行。

代碼

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long  long
#define db double
#define EPS 1e-15
#define inf 1e10

using namespace std;

const int N=100010;
int from[200020],to[200020];
int edge[200020],head[200020];
int n,m,cnt;
bool vis[200020];
void add(int x,int y){
    from[cnt]=x; to[cnt]=y;
    edge[cnt]=head[x];
    head[x]=cnt++;
    from[cnt]=y; to[cnt]=x;
    edge[cnt]=head[y];
    head[y]=cnt++;
}
int dfs(int x,int f){
    queue<int> q;
    for (int i=head[x];i!=-1;i=edge[i]){
        int nx=to[i];
        if (vis[i] || nx==f) continue;
        vis[i]=vis[i^1]=1;
        int tmp=dfs(nx,x);
        if (tmp)
            printf("%d %d %d\n",x,nx,tmp);
        else {
            q.push(nx);
        }
    }
    while (q.size()>=2){
        int a=q.front();
        q.pop();
        int b=q.front();
        q.pop();
        printf("%d %d %d\n",a,x,b);
    }
    while (!q.empty()){
        int a=q.front();
        q.pop();
        return a;
    }
    return 0;
}
int main(){
    while (scanf("%d%d",&n,&m)!=EOF){
        cnt=0;
        memset(vis,0,sizeof(vis));
        memset(head,-1,sizeof(head));
        int x,y;
        for (int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            add(x,y);
        }
        if (m&1) {
            puts("No solution");
            continue;
        }
        dfs(1,-1);
    }
    return 0;
}

總結:

1、需要注意一下其他問題到圖論問題的轉化上,比如差分約束和第一題這種帶偏序關係的題目。

2、注意代碼實現的細節,第四題爲了使用結構體的優先隊列,重載了運算符,卻沒有傳進去引用,容易GG,而且最好不要這麼寫,優先隊列有三個參數,可以自己寫個仿函數傳進去。同樣是第四題,沒有注意函數定義的順序,起初定義的find函數在引用函數的下面,所以編譯器自動給我用了類裏的find函數,導致莫名CE。

3、注意讀題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章