• wannafly camp day1


    题目描述:

    恬恬的生日临近了。宇扬给她准备了一个 蛋糕。

    正如往常一样,宇扬在蛋糕上插了nnn支蜡烛,并把蛋糕分为mmm个区域。因为某种原因,他必须把第iii根蜡烛插在第aia\_iai个区域或第bib\_ibi个区域。区域之间是不相交的。宇扬在一个区域内同时摆放xxx支蜡烛就要花费x2x^2x2的时间。宇扬布置蛋糕所用的总时间是他在每个区域花的时间的和。

    宇扬想快些见到恬恬,你能告诉他布置蛋糕最少需要多少时间吗?

    输入:

    第一行包含两个整数nnn,mmm(1≤n≤501 le n le 501n50, 2≤m≤502le mle 502m50)。

    接下来nnn行,每行两个整数ai,bia\_i,b\_iai,bi1≤ai,bi≤m1 le a\_i, b\_i le m1ai,bim)。

    输出:

    一个整数表示答案。

    样例输入
    3 3
    1 2
    1 2
    1 2
    样例输出
    5
    描述

    题目描述:

    恬恬的生日临近了。宇扬给她准备了一个 蛋糕。

    正如往常一样,宇扬在蛋糕上插了nnn支蜡烛,并把蛋糕分为mmm个区域。因为某种原因,他必须把第iii根蜡烛插在第aia\_iai个区域或第bib\_ibi个区域。区域之间是不相交的。宇扬在一个区域内同时摆放xxx支蜡烛就要花费x2x^2x2的时间。宇扬布置蛋糕所用的总时间是他在每个区域花的时间的和。

    宇扬想快些见到恬恬,你能告诉他布置蛋糕最少需要多少时间吗?

    输入:

    第一行包含两个整数nnn,mmm(1≤n≤501 le n le 501n50, 2≤m≤502le mle 502m50)。

    接下来nnn行,每行两个整数ai,bia\_i,b\_iai,bi1≤ai,bi≤m1 le a\_i, b\_i le m1ai,bim)。

    输出:

    一个整数表示答案。

    题意 :

      给你 n 根蜡烛,同时告诉你每根蜡烛可以插在哪个区域中,对于同一个区域内插入的蜡烛总的花费为此区域内蜡烛数量的平方,问最小的花费是多少

    思路分析 :

      贪心对于此问题是不正确的,考虑一下网络流建图

      考虑费用流时把每个part拆成n个点,选择第i个点的代表为放置i块蛋糕和(i - 1)块蛋糕的时间差,这个时间差是递增的,因此在费用流的过程中必定会从小到大选择
    具体建图:左边n个点代表n个蛋糕,右边m * n个点代表m个part,每个part拆成n个点。源点向每个左边的点连一条流量1费用0的边,每个右边的点向汇点连一条流量1费用0的编。每个蛋糕向可以放的两个part的所有点连边,连向第i个点的费用为i^2 - (i - 1)^2,流量为1。这样求最小费用流既为答案。

    代码示例 :

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e4;
    const int maxm = 1e5;
    const int inf = 0x3f3f3f3f;
    struct Edge
    {
        int to,next,flow,cost; // flow 表示水现有的流量
    }edge[maxm];
    int head[maxn],tol;
    int pre[maxn],dis[maxn];
    bool vis[maxn];
    int N; //节点个数,编号0->N-1 !全局变量 需要init赋值或主函数改变
    
    void init(int n)
    {
        N=n;
        tol = 0;
        memset(head,-1,sizeof(head));
    }
    
    void addedge(int u,int v,int cap,int cost) //边起点,终点,流量,费用
    {
        edge[tol].to = v;
        edge[tol].cost = cost;
        edge[tol].flow = cap;
        edge[tol].next = head[u];
        head[u] = tol++;
        edge[tol].to = u;
        edge[tol].cost = -cost;
        edge[tol].flow = 0;
        edge[tol].next = head[v];
        head[v] = tol++;
    }
    
    bool spfa(int s,int t)     //单源最短路径算法 可判断负环
    {
        queue<int >q;
        for(int i=0;i<N;i++)
        {
            dis[i] = inf;
            vis[i] = false;
            pre[i] = -1;
        }
        dis[s] = 0;
        vis[s] = true;
        q.push(s);
        while(!q.empty())
        {
            int u = q.front();
            q.pop();
            vis[u] = false;
            for(int i=head[u];i!=-1;i=edge[i].next)
            {
                int v= edge[i].to;
                if(edge[i].flow && dis[v]>dis[u]+edge[i].cost)
                {
                    dis[v] = dis[u] + edge[i].cost;
                    pre[v] = i;
                    if(!vis[v])
                    {
                        vis[v] = true;
                        q.push(v);
                    }
                }
            }
        }
        if(pre[t]==-1) return false;
        else return true;
    }
    
    int MCMF(int s,int t,int &cost)  //MinCostMaxFlow  返回最大流,cost存最小费用
    {
        int flow = 0;
        cost = 0;
        while(spfa(s,t))
        {
            int Min = inf;
            for(int i= pre[t];i!=-1;i=pre[edge[i^1].to])
            {
                if(Min>edge[i].flow)
                    Min=edge[i].flow;
            }
            for(int i= pre[t];i!=-1;i=pre[edge[i^1].to])
            {
                edge[i].flow -= Min;
                edge[i^1].flow +=Min;
                cost += edge[i].cost*Min;
            }
            flow += Min;
        }
        //printf("++++ %d  %d 
    ", flow, tol);
        return flow;
    }
    
    int n, m;
    
    int main() {
        int a, b;
        cin >> n >> m;
        init(n+m+2); 
        for(int i = 1; i <= n; i++){
            scanf("%d%d", &a, &b);
            addedge(0, i, 1, 0);
            addedge(i, n+a, 1, 0);
            addedge(i, n+b, 1, 0);    
        }
        for(int i = n+1; i <= n+m; i++){
            for(int j = 1; j <= 99; j += 2){
                addedge(i, n+m+1, 1, j);
            }
        } 
        int ans;
        MCMF(0, n+m+1, ans);
        printf("%d
    ", ans);
        return 0;
    }
    

    题目描述:

    弱弱有两个属性aaa和bbb,这两个属性初始的时候均为000,每一天他可以通过努力,让aaa涨111点或bbb涨111点。

    为了激励弱弱努力学习,我们共有nnn种奖励,第i种奖励有xix\_ixiyiy\_iyiziz\_izi三种属性,若a≥xiage x\_iaxib≥yibge y\_ibyi,则弱弱在接下来的每一天都可以得到ziz\_izi的分数。

    问m天以后弱弱最多能得到多少分数。

    输入:

    第一行一个两个整数nnn和mmm(1≤n≤10001le nle 10001n1000,1≤m≤20000000001le mle 20000000001m2000000000)。

    接下来nnn行,每行三个整数

    xix\_ixiyiy\_iyiziz\_izi1≤xi,yi≤10000000001le x\_i,y\_ile 10000000001xi,yi1000000000,1≤zi≤10000001le z\_i le 10000001zi1000000)。

    输出:

    一行一个整数表示答案。

    样例输入
    2 4
    2 1 10
    1 2 20
    样例输出
    50

    题意 :有两个属性 a 和 b , 初始值均为 0 ,在每一天你可以让 a 或 b 的值去加 1 ,当你到达某一个临界点时,会得到一些分数,问 m 天后最多能得到多少分?

    思路分析 :

      若天数 m 最大是1000,一个比较好想的 dp 就是 dp[ i ][ j ] 表示 a 属性增加到 i 点, b 属性增加到 j 点的最大得分

      那么 dp[i][j] = max(dp[i-1][j]+v[i][j], dp[i][j-1]+v[i][j]) , v[i][j] 表示 a 属性到达 i , b 属性到 j 后每天的得分, 预处理一下即可

      (i, j) 此项得分确实有 则 v[i][j] = v[i-1][j]+v[i][j-1]-v[i-1][j-1]+z[i][j] , 否则 v[i][j] = v[i-1][j]+v[i][j-1]-v[i-1][j-1] ;

        

      但是此题的 天数 m 给了很大的,上面形式的dp 是行不通的,但是观察一下数据范围 a 属性和 b 属性的种类确实最多有 1000, 因此我们这里换种形式 dp

      定义dp[i][j] 表示 a 属性到达第 i 个, b属性到达第 j 个的最大得分 , v[i][j] 也是类似的定义 

    代码示例:

    using namespace std;
    #define ll long long
    const ll maxn = 1e6+5;
    const ll mod = 1e9+7;
    const double eps = 1e-9;
    const double pi = acos(-1.0);
    const ll inf = 0x3f3f3f3f;
    typedef pair<ll, ll> P;
    
    ll n, m;
    ll xx[1005], yy[1005];
    ll v[1005][1005], dp[1005][1005];
    map<P, ll>mp;
    set<ll>s1, s2;
    
    int main() {
        //freopen("in.txt", "r", stdin);
        //freopen("out.txt", "w", stdout);
        cin >> n >> m;
        ll xi = 1, yj = 1;
        ll x, y, z;
        
        for(ll i = 1; i <= n; i++){
            scanf("%lld%lld%lld", &x, &y, &z);
            if (mp.count(P(x, y)) == 0) mp[P(x, y)] = z;
            else mp[P(x, y)] += z;
            
            if (s1.count(x) == 0) {s1.insert(x); xx[xi++] = x;}
            if (s2.count(y) == 0) {s2.insert(y); yy[yj++] = y;}        
        }
        
        sort(xx+1, xx+xi);
        sort(yy+1, yy+yj);
        for(ll i = 1; i < xi; i++){
            for(ll j = 1; j < yj; j++){
                if (mp.count(P(xx[i], yy[j]))) v[i][j] = v[i-1][j]+v[i][j-1]-v[i-1][j-1]+mp[P(xx[i],yy[j])];
                else v[i][j] = v[i-1][j]+v[i][j-1]-v[i-1][j-1];
                //prllf("---- %d %d %d   %d %d
    ", i, j, v[i][j], x[i], y[j]);
            }
        }
        
        for(ll i = 1; i < xi; i++){
            for(ll j = 1; j <yj; j++){
                ll x1 = dp[i-1][j]+(xx[i]-xx[i-1]-1)*v[i-1][j]+v[i][j];
                ll x2 = dp[i][j-1]+(yy[j]-yy[j-1]-1)*v[i][j-1]+v[i][j];
                dp[i][j] = max(x1, x2); 
                //prllf("++++ %d %d %d 
    ", i, j, dp[i][j]);
            }
        }
        
        ll ans = 0;
        for(ll i = 1; i < xi; i++){
            for(ll j = 1; j < yj; j++){
                ll res = dp[i][j] + (m-xx[i]-yy[j])*v[i][j];
                ans = max(ans, res);
            }
        }
        printf("%lld
    ", ans);
        
        return 0;
    }
    
    东北日出西边雨 道是无情却有情
  • 相关阅读:
    (一)Python入门-3序列:04列表-元素删除的3种方式-删除本质是数组元素拷贝
    (一)Python入门-3序列:05列表-元素的访问-元素出现次数统计-成员资格判断
    Java学习笔记_180704_final和static关键字
    Java学习笔记_180702_基本类型和引用类型作为参数传递特性
    Java学习笔记_180702_面向对象编程
    如何手动实现整型数值60的二进制到十六进制的转换
    Java学习笔记_180627_循环控制语句
    兔子生兔子问题(斐波那契数列)
    Java学习笔记_180625_基础语法
    ubuntu和win10双系统,用ubuntu引导win10启动
  • 原文地址:https://www.cnblogs.com/ccut-ry/p/9434398.html
Copyright © 2020-2023  润新知