Codeforces Round #214 (Div. 2)(揹包變形)

u010660276發表於2014-01-14

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






相關文章