周练补题

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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值