【日记】1.1

1.1

贪心

1.1283E:New Year Parties

题意:有n个人,分别在1-n之间的点上,每个人最多往左或往右走一个格子,问走完之后最少占据格子和最多占据格子数分别是多少。

思路:贪心。最多:考虑每个位置怎么被占据,首先判断前面一个格子,如果还有剩的就拉过来,再看当前格子,如果有就占据,再看右边的格子,如果有就拉过来。可以用反证法说明这么做是最优的。

最少:考虑每个位置的数怎么丢出去。vis数组记录每个位置被占据的情况。如果前一个位置必须被占据,就丢到上一个。否则就丢到下一个位置,并占据下一个位置。可以用反证法说明这么做是最优的。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mid ((l+r)>>1)
#define db(x) cout<<#x<<":"<<x<<endl;
const int M=2e5+20,P=1e9+7;
struct TTTT{
    int n,num1[M],num2[M],vis[M];
    void init(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            int c;
            scanf("%d",&c),++num1[c];
        }
        for(int i=1;i<=n;++i)
            num2[i]=num1[i];
    }
    void run(){
        init();
        int ans=0;
        for(int i=1;i<=n;++i){
            if (num2[i]==0||vis[i])
                continue;
            if(vis[i-1])
                num2[i]=0;
            else
                vis[i+1]=1,++ans;
        }
        printf("%d ",ans);
        ans=0;
        if (num1[1])
            --num1[1],++ans;
        for(int i=1;i<=n;++i)
            if (num1[i-1])
                --num1[i-1],++ans;
            else if (num1[i])
                --num1[i],++ans;
            else if (num1[i+1])
                --num1[i+1],++ans;
        if (num1[n])
            --num1[n],++ans;
        printf("%d\n",ans);
    }
}TTT;
int main(){
    TTT.run();
    return 0;
}

DP

1.1271D:Portals

题意:你一开始有k个兵,要顺次打n个城堡,打完城堡之后不会损失兵。每个城堡有a,b,c三个参数,表示必须拥有>=a个兵才能打下来,打完之后可以获得b个兵,之后如果分出来1个兵占领这个城堡,那么就可以获得c点分数,如果不占领就不获得分数。

这里还有一些神奇的portal,有u和v两个参数,表示当你站在u城堡的时候,可以传送一个兵到v去占领v。注意不可以连续传送,比如你站在4,不可以4-3再3-1,只能用所在城堡直接相连的portal。

要求必须n个城堡全部打下来才可以,如果不能,输出-1,否则输出打完n个城堡之后可以获得的最大分数。

思路:我是DP做法。首先不考虑portal,那么dp[i] [j]表示打完前i个城堡之后,还剩下j个兵的状态,能得到的最大分数。那么刷表即可,每一个状态可以有两种选择,打完留兵或者打完不留,dp[i] [j+b[i]]=max(当前, dp[i-1] [j]),dp[i] [j+b[i]-1]=max(当前, dp[i-1] [j]+c[i])。时间复杂度\(O(n\sum k)\)

现在考虑portal。如果不占领第i个城堡,就不影响。利用贪心易知,如果要派兵占领第i个城堡,而且存在portal使得可以从之后的城堡j,k,l,……传送回来,那么选择之后城堡里面编号最大的那个往回传送一定是最优的。感性理解一下,留兵的唯一不利是有可能往前打的过程中兵不够打不了了,所以上述选择能够让那个本来直接留下的兵多打几轮(而且是尽可能多打了),再回来防守,所以肯定不会变差。

所以现在走到第i个城堡之后,选择变多了,首先是是否在本地留兵,之后是往回传送几个兵。

v[i]表示城堡i往回传送的目的地(可以看代码看具体意义)。那么还是贪心,肯定先选那些c值大的城堡传送,因此就先排序,再前缀和,就依次是往回传送1,2,3,4,5……个兵的最大获得的分数了。

细节比较多,需要分类讨论一下,详见代码。

时间复杂度不变,仍然是\(O(n\sum k)\),只不过要注意,这里的dp[i] [j]不再是原来子问题了,即前i的城堡剩下j个兵的最大分数,这是因为已经贪心处理portal的原因。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mid ((l+r)>>1)
#define db(x) cout<<#x<<":"<<x<<endl
#define qmax(x,y) (x)=max((x),(y))
const int M=5e3+50,P=1e9+7;
int n,m,k,a[M],b[M],c[M],dp[M][M],pfrom[M];
vector<int> v[M];
struct cmp{
    bool operator()(int x,int y){
        return c[x]>c[y];
    }
};
struct TTTT{
    void init(){
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;++i)
            scanf("%d%d%d",&a[i],&b[i],&c[i]);
        for(int i=1;i<=m;++i){
            int j,k;
            scanf("%d%d",&j,&k),pfrom[k]=max(pfrom[k],j);
        }
        for(int i=1;i<=n;++i)
            if(pfrom[i])
                v[pfrom[i]].push_back(i);
    }
    void run(){
        init();
        int kmin=k,kmax=k;
        for(int i=1;i<=n;++i){
            kmin=max(kmin,a[i]);
            if (kmin>kmax){
                printf("-1\n");
                return;
            }
            if (!v[i].empty()){
                sort(v[i].begin(),v[i].end(),cmp());
                v[i][0]=c[v[i][0]];
                for(int k=1;k<v[i].size();++k)
                    v[i][k]=c[v[i][k]]+v[i][k-1];
            }
            for(int j=kmin;j<=kmax;++j){
                qmax(dp[i][j+b[i]],dp[i-1][j]);
                if (!v[i].empty())
                    for(int k=0;k<v[i].size()&&j+b[i]-k-1>=0;++k)
                        qmax(dp[i][j+b[i]-k-1],dp[i-1][j]+v[i][k]);
                if (!pfrom[i]){
                    if (j+b[i]-1>=0)
                        qmax(dp[i][j+b[i]-1],dp[i-1][j]+c[i]);
                    if (!v[i].empty())
                        for(int k=0;k<v[i].size()&&j+b[i]-1-k-1>=0;++k)
                            qmax(dp[i][j+b[i]-1-k-1],dp[i-1][j]+c[i]+v[i][k]);
                }
            }
            kmax+=b[i],kmin+=b[i]-v[i].size()-(!pfrom[i]?1:0);
        }
        int ans=0;
        for(int i=kmin;i<=kmax;++i){
            qmax(ans,dp[n][i]);
        }
        printf("%d\n",ans);
    }
}TTT;
int main(){
    TTT.run();
    return 0;
}

最后这个题好搞啊,写了好久。

猜你喜欢

转载自www.cnblogs.com/diorvh/p/12129744.html
1.1