Codeforces Round #214 (Div. 2)(背包变形)

A. Dima and Guards

只需要比较一下,守卫的最小要求的和与给定值的大小即可

#include<iostream>
#include<cstdio>
using namespace std;
int n,x1,y1,x2,y2;
int who,choc,juice;
bool can(int x,int y)
{
    return x+y<=n;
}
int main()
{
    cin>>n;
    bool flag=false;
    for(int i=1;i<=4;i++)
    {
        cin>>x1>>y1>>x2>>y2;
        if(!flag)
        {
            if(can(x1,x2)){choc=x1,juice=n-choc;flag=true;}
            else if(can(x1,y2)){choc=x1;juice=n-choc;flag=true;}
            else if(can(y1,x2)){choc=y1;juice=n-choc;flag=true;}
            else if(can(y1,y2)){choc=y1,juice=n-choc;flag=true;}
            if(flag) who=i;
        }
    }
    if(flag)
    cout<<who<<" "<<choc<<" "<<juice<<endl;
    else cout<<-1<<endl;
    return 0;
}
B. Dima and To-do List

表示没太看懂。。。只需要从1到k循环就行了

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100010;
const int INF=1000000000;
int a[2*maxn];
int N,K;
int main()
{
    scanf("%d%d",&N,&K);
    for(int i=1;i<=N;i++)
        scanf("%d",&a[i]);
    int max1=INF,sum=0,ans;
    for(int i=1;i<=K;i++)
    {
        sum=0;
        for(int j=0,k=i;j<N/K;k+=K,j++)
        {
            sum+=a[k];
        }
        if(sum<max1){max1=sum,ans=i;}
    }
    cout<<ans<<endl;
}

C. Dima and Salad

没想到需要这样变形,看了别人的题解,才明白过来

题意:就是给了你两个数组:a数组和b数组,然后让你从a数组中选取一个的子序列,子序列的和为sum1,然后除以相应b数组子序列的和sum2,sum1/sum2刚好为k,让你求出满足要求的a数组中sum1最大为多少,如果不存在这样的子序列,那么就输出-1,否则输出a数组中最大的sum1。

分析:对于这道题,我感悟还是比较深的,当时我想到用dp去做,并且抓住了两个数组加起来的和分别最多为10000,当时我没有把题目中的那个式子做变形,所以只想到了去记录a数组子序列相加之后的状态,但是如果a数组子序列的一个状态对应有多个b数组序列的状态呢?想到了这里,我就没法想下去了,因为我无法想到一个好的方法去解决这个问题,后来比完赛之后去看了下别人的代码,他们是把式子变形之后,把a[i]-b[i]*k作为状态进行dp的,这样a数组的序列和b数组的序列就绑定在一起了,就没必要去考虑我出现的问题了,而他的状态变化范围是:-10000-10000,出现了为负数的状态,无法用数组实现,于是干脆把所有状态都加上10000,于是状态的变化范围就变成:0-20000,那么现在就可以在此基础上进行dp了

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#pragma comment (linker,"/STACK:102400000,102400000")
#define maxn 1005
#define MAXN 300005
#define mod 1000000007
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-6
typedef long long ll;
using namespace std;

int n,m,ans,k,flag,cnt;
int a[maxn],b[maxn];
int w[maxn];
int dp[MAXN];

bool solve()
{
    int i,j;
    memset(dp,-INF,sizeof(dp));
    dp[10000]=0;
    for(i=1; i<=n; i++)
    {
        if(w[i]>=0)
        {
            for(j=20000; j>=w[i]; j--)
            {
                if(dp[j-w[i]]!=-INF) dp[j]=max(dp[j],dp[j-w[i]]+a[i]);
            }
        }
        else
        {
            for(j=0;j<20000;j++)
            {
                if(dp[j-w[i]]!=-INF) dp[j]=max(dp[j],dp[j-w[i]]+a[i]);
            }
        }
    }
    ans=dp[10000];
    if(ans==0) return false ;
    return true ;
}
int main()
{
    int i,j;
    while(~scanf("%d%d",&n,&k))
    {
        for(i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
        }
        for(i=1; i<=n; i++)
        {
            scanf("%d",&b[i]);
        }
        for(i=1; i<=n; i++)
        {
            w[i]=a[i]-k*b[i];
        }
        if(solve()) printf("%d\n",ans);
        else printf("-1\n");
    }
    return 0;
}

D. Dima and Trap Graph
思路:枚举每条边,并查集维护,每次加进去右端点大的边判断1和n是不是已联通,更新最大值
下面是并查集的代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxm=3010;
const int maxn=1010;
struct node
{
    int a,b,l,r;
};
int pre[maxn];
int n,m;
bool cmp(node a,node b)
{
    return a.r>b.r;
}
void init()
{
    for(int i=0;i<=n;i++)
    {
        pre[i]=i;
    }
}
int find(int x)
{
    if(pre[x]==x) return x;
    return pre[x]=find(pre[x]);
}
void unite(int a,int b)
{
    int x=find(a);
    int y=find(b);
    pre[x]=y;
}
int main()
{
    node edge[maxm];
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    cin>>edge[i].a>>edge[i].b>>edge[i].l>>edge[i].r;
    sort(edge,edge+m,cmp);
    int ans=-1;
    for(int i=0;i<m;i++)
    {
        init();
        int min1=edge[i].r;
        for(int j=0;j<m;j++)
        {
            if(edge[j].l>edge[i].l) continue;
            unite(edge[j].a,edge[j].b);
            min1=min(min1,edge[j].r);
            int x=find(1);
            int y=find(n);
            if(x==y) {ans=max(ans,min1-edge[i].l+1);break;}
        }
    }
    if(ans<=0)
    cout<<"Nice work, Dima!"<<endl;
    else
    cout<<ans<<endl;
    return 0;
}

这个题看人家写的还可以枚举左端点,二分右顶点,dfs判断。
下面是代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 1005
#define MAXN 100005
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;

int n,m,ans,cnt,ii,cxx,flag;
int pp[MAXN];
bool vis[MAXN];
int x[MAXN];
struct Node
{
    int v,next;
    int a,b;
} edge[MAXN];

void addedge(int u,int v,int a,int b)
{
    cnt++;
    edge[cnt].v=v;
    edge[cnt].a=a;
    edge[cnt].b=b;
    edge[cnt].next=pp[u];
    pp[u]=cnt;
}
void dfs(int u,int le,int ri)
{
    if(flag) return ;
    if(u==n)
    {
        flag=1;
        return ;
    }
    int i,j,v;
    for(i=pp[u]; i; i=edge[i].next)
    {
        v=edge[i].v;
        if(!vis[v]&&le>=edge[i].a&&ri<=edge[i].b)
        {
            vis[v]=1;
            dfs(v,le,ri);
        }
    }
}
void solve()
{
    int i,j,u,v,t;
    int le,ri,mid;
    ans=0;
    for(i=1;i<=cxx;i++)
    {
        le=x[i],ri=1000001;
        while(le<ri)
        {
            mid=(le+ri)>>1;
            flag=0;
            memset(vis,0,sizeof(vis));
            vis[1]=1;
            dfs(1,x[i],mid);
            if(flag) le=mid+1;
            else ri=mid;
        }
        ans=max(ans,le-x[i]);
    }
}
int main()
{
    int i,j;
    int u,v,a,b;
    while(~scanf("%d%d",&n,&m))
    {
        cnt=cxx=0;
        memset(pp,0,sizeof(pp));
        for(i=1; i<=m; i++)
        {
            scanf("%d%d%d%d",&u,&v,&a,&b);
            x[++cxx]=a;
            x[++cxx]=b;
            addedge(u,v,a,b);
            addedge(v,u,a,b);
        }
        solve();
        if(ans) printf("%d\n",ans);
        else printf("Nice work, Dima!\n");
    }
    return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值