周練補題

D:D - Knight Tournament CodeForces - 356A
題意:編號1到n的人打架,給出m個區間, 編號位於這個區間中的人打架,再給出這個區間中最後勝利的人,對於每個人,求打敗他們的人的編號。
思路一:線段樹維護一個區間的勝者編號,由於每次只有第一次被打敗有效,因此要倒着遍歷區間,從這個區間的勝者的編號爲中點,分成兩個區間分別進行維護。但是我腦子卡,非要正着搞。
思路二(嫖的,大佬就是大佬,思路刁鑽):暴力的優化,如果正常暴力操作的話,我們每次都對每個區間給定的範圍都進行遍歷,判斷其中的每一個值是否被打敗過,沒有就更新,有就跳過,所以這樣會進行很多重複操作,導致複雜度O(n^2),所以爲了優化,已經得到答案的這些編號我們想辦法去除掉,後來再進行更新的時候就用管這些值了。用set來表示未更新答案的集合,每更新一個答案就去除一個,同時記錄下答案就行。

//set暴力優化
#include<bits/stdc++.h>
#define mod (10007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef long double ld;
const int inf_max = 0x3f3f3f;
const ll Linf = 9e18;
const int maxn = 3e5+5;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
    int f=1,res=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
    return f*res;
}
struct MATCH
{
    int l,r,x;
}match[maxn];
int n,m,ans[maxn];
set<int>st;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) st.insert(i);
    for(int i=1;i<=m;i++) {
        int l,r,w;
        scanf("%d%d%d",&l,&r,&w);
        set<int>::iterator iter=st.lower_bound(l);
        while((*iter)<=r&&iter!=st.end()) {
            ans[(*iter)]=w;
            //cout<<(*iter)<<" "<<w<<endl;
            iter=st.erase(iter);
        }
        st.insert(w);
    }
    set<int>::iterator iter=st.begin();
    ans[(*iter)]=0;
    for(int i=1;i<=n;i++) {
        printf("%d%c",ans[i],i==n?'\n':' ');
    }
    return 0;
}

F - Fox And Jumping
題意:用最少的花費選出一些卡片使得它們的最大gcd爲1;
思路:看出來了感覺還可以,就是l太大了不好開dp數組,在這裏學到了一種操作:用map開dp數組就不會曝內存.
dp[i][j]:前i個數最大公因數爲j的最小cost;
dp[i][gcd(j,l[i])]=min(dp[i-1][j]+cost[i]); 其中l[i]是第i張卡片的長度。

#include<bits/stdc++.h>
#define mod (10007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef long double ld;
const int inf_max = 0x3f3f3f;
const ll Linf = 9e18;
const int maxn = 300+5;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
    int f=1,res=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
    return f*res;
}

int n,l[maxn],c[maxn];
map<int,int>dp;
int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}
int main()
{
    dp.clear();
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&l[i]);
    for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    for(int i=1;i<=n;i++) {
        dp[l[i]]=c[i];  //這裏初始化要注意一下,因爲我們可以從任意一個數開始選,這裏的意思就是我們就只選當前這個數
        //if(i==1) continue;
        for(auto j:dp) {
            int t=gcd(j.first,l[i]);
            if(dp.count(t) == 0) dp[t]=j.second+c[i];
            dp[t]=min(dp[t],j.second+c[i]);
        }
    }
    if(dp.count(1)) cout<<dp[1]<<endl;
    else cout<<-1<<endl;
    return 0;
}

E - Destroying the bus stations HDU - 2485
題意:給n個點m條邊的有向圖和一個參數k,問最少刪幾個點可以讓從1~n的最短路所花的時間大於k。
思路:這題好像是最大流什麼來做,但是學長說這個方法是錯誤的,正確方法應該是暴力枚舉+spfa。還沒學最大流,也看不出來這個題對不對T~T。暴力枚舉刪除的點然後看剩下的點的最短路能否大於k,可以的話就是答案;這裏的暴力採取的是迭代加深的方法,以前看過,不過感覺沒怎麼用,給忘了 我g(果然還是做的題太少了)。這個迭代加深就好像bfs+dfs 在縱向找答案的時候,也橫向找,但是規定一個縱向的深度,到這個深度還沒有找到就返回false;然後給下一個深度來繼續枚舉。這樣做的好處就是,因爲dfs會一直找到盡頭,然而我們的答案可能在某個深度橫着找一下就有了,所以我們限制着深度同時橫着找。迭代加深適合的數據量較小,這道題可以這樣我也是醉了。
其次我們只刪除最短路上的點,因爲其他的點刪了對答案也不會影響。
還有一點就是在跑spfa的時候,由於路徑長度是1,所以我們只要遍歷到了一個點,那麼這條路徑就一定是最短的了,所以一個點入隊一次就行了,這個就像樹的層次遍歷,只不過,樹裏面兩點間只有一條路徑所以只需要跑一次。

#include<bits/stdc++.h>
#define mod (10007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef long double ld;
const int inf_max = 0x3f3f3f;
const ll Linf = 9e18;
const int maxn = 5000+5;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
    int f=1,res=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
    return f*res;
}
int n,m,k,head[100],vis[100],cnt,del[100],dis[100],pre[100],path[100][100];
struct EDGE
{
    int v,last;
}edge[maxn];
void Initial()
{
    memset(edge,0,sizeof(edge));
    cnt=0;
    memset(head,-1,sizeof(head));
}
void add_edge(int u,int v)
{
    edge[cnt].v=v;
    edge[cnt].last=head[u];
    head[u]=cnt++;
}
void SPFA(int start)
{
    memset(pre,-1,sizeof(pre));
    memset(dis,inf_max,sizeof(dis));
    memset(vis,0,sizeof(vis));
    queue<int>q;
    while(!q.empty()) q.pop();
    dis[start]=0;pre[start]=-1;
    q.push(start);vis[start]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];~i;i=edge[i].last) {
            int v=edge[i].v;
            if(del[v]||vis[v]) continue;  //如果這個點被刪了或者這個點已經跑過了,就跳過;
            dis[v]=dis[u]+1;
            pre[v]=u;  //路徑的保存;
            q.push(v);vis[v]=1;
        }
    }
}
bool dfs(int dep,int limit)
{
    SPFA(1);
    if(dis[n]>k) return true;  //達到條件返回true
    if(dep==limit) return false;  //規定深度到達還未滿足條件,返回false
    int j=n;
    while(j!=-1) {
        path[dep][j]=pre[j];
        j=pre[j];
    }//因爲在下個深度時跑spfa會把pre清空,所以我們要把每個深度的路徑保留下來,方便在每一層對路徑的刪除進行操作
    int i=path[dep][n];  
    bool flag=false;
    while(i!=1) {  //枚舉刪除的點
        if(!del[i]) {
           del[i]=1;  //路徑刪掉
           flag=(flag||dfs(dep+1,limit));  
           del[i]=0; //路徑復原
        }
        i=path[dep][i];
    }
    return flag;
}
int main()
{
    while(scanf("%d%d%d",&n,&m,&k),n+m+k)
    {
        Initial();
        for(int i=1;i<=m;i++) {
            int u,v;
            scanf("%d%d",&u,&v);
            add_edge(u,v);
        }
        for(int i=0;i<=n;i++) {
            memset(del,0,sizeof(del));
            memset(path,0,sizeof(path));
            if(dfs(0,i)) {
                printf("%d\n",i);
                break;
            }
        }
    }
    return 0;
}

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