CCPC-Wannafly Summer Camp #1(部分解题报告)

A:Birthday

时间限制: 1 Sec  内存限制: 256 MB

题目描述

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

正如往常一样,宇扬在蛋糕上插了n支蜡烛,并把蛋糕分为m个区域。因为某种原因,他必须把第i根蜡烛插在第ai个区域或第bi个区域。区域之间是不相交的。宇扬在一个区域内同时摆放x支蜡烛就要花费x2的时间。宇扬布置蛋糕所用的总时间是他在每个区域花的时间的和。

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

输入

第一行包含两个整数nm(1 ≤ n ≤ 50, 2 ≤ m ≤ 50)。

接下来n行,每行两个整数ai, bi(1 ≤ ai, bim)。

输出

一个整数表示答案.

样例输入

3 3
1 2
1 2
1 2

样例输出

5

唉,还是大佬的题解写的好,所以感谢wls大佬分享思路  Orz。。。

思路:考虑费用流时把每个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;
#define M 1100
const int inf=0x7fffffff;
struct node{
    int u,v,c,f,next;  //C为花费,F为flow流量
}e[M*40];
int pre[M],dis[M],head[M],t;
int vis[M];

void add1(int u,int v,int c,int f){
    e[t].u=u;
    e[t].v=v;
    e[t].c=c;
    e[t].f=f;
    e[t].next=head[u];
    head[u]=t++;
}

void add(int u,int v,int c,int f){
    add1(u,v,c,f);
    add1(v,u,-c,0);  //反向边流量初始为零,如果走反向边费用正好和原边抵消
}

int spfa(int s,int t){
    int i,u,v;
    queue<int>q;
    q.push(s);
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    for(i=s;i<=t;i++)
        dis[i]=inf;
    dis[s]=0;
    while(!q.empty()){
        u=q.front();
        q.pop();
        for(i=head[u];i!=-1;i=e[i].next)
        {
            v=e[i].v;
            if(e[i].f&&dis[v]>dis[u]+e[i].c)  //找到一条最小费用流
            {
                dis[v]=dis[u]+e[i].c;
                pre[v]=i;       //记录路径
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
        vis[u]=0;
    }
    if(dis[t]!=inf)
        return 1;
    return 0;
}

void solve(int s,int t){
    int ans=0,i,j;
	int flow=0,cost=0;     //总流量、总费用
    while(spfa(s,t)){
		int minf=inf;
		for(i=pre[t];i!=-1;i=pre[e[i].u]){
			if(e[i].f<minf)
				minf=e[i].f;
		}
		flow+=minf;   //该条路径的流量
        for(i=pre[t];i!=-1;i=pre[e[i].u]){
            j=i^1;
            e[i].f-=minf;
            e[j].f+=minf;
        }
        cost+=dis[t]*minf;   //单位运费和乘以流量得费用
    }
    printf("%d\n",cost);
}
int main(){
    int i,u,v,c,n,m;
    while(scanf("%d%d",&n,&m)==2){
        t=0;
        memset(head,-1,sizeof(head));
        for(i=1;i<=n;i++){
            scanf("%d%d",&u,&v);
            add(0,i,0,1);
            add(i,u+n,0,1);
            add(i,v+n,0,1);
        }
        for(int i=1 ; i<=m ; ++i){
            for(int j=1 ; j<=99 ; j+=2){//99 = 2*50-1;
                add(i+n,n+m+1,j,1);
            }
        }
        solve(0,n+m+1);
    }
    return 0;
}

B: Board

时间限制: 1 Sec  内存限制: 256 MB

题目描述

恬恬有一个n × n的数组。她在用这个数组玩游戏:

开始时,数组中每一个元素都是0。

恬恬会做某些操作。在一次操作中,她可以将某一行的所有元素同时加上一个值,也可以将某一列的所有元素同时加上一个值。

在几次操作后,一个元素被隐藏了。你能帮助她回忆隐藏的数是几吗?

输入

第一行一个整数n(1 ≤ n ≤ 1000)。

接下来n行每行n个整数表示数组a

第(i + 1)行的第j个元素表示aijaij =  - 1或0 ≤ aij ≤ 10000)。 - 1表示隐藏的元素。

输出

仅一个整数表示答案。

样例输入

3
1 2 1
0 -1 0
0 1 0

样例输出

1

这题思路同样为wls大佬给出,Orz wls 。。。

把格子N染色,第i行第j列格子的颜色为(i + j) % N。那么每次操作时,必定是N种不同的颜色都有一格被操作到,

因此最后任何颜色格子的和必定是相等的。因此只需要记录每种颜色格子的和,

并算出缺失格子的颜色C,用其余颜色的和减去颜色C的和即可

#include<bits/stdc++.h>
 
using namespace std;
int arr[1010][1010];
int a[1010], b[1010];
 
int main()
{
    int n;
    while(~scanf("%d", &n)){
        memset(a, 0, sizeof(a));
        memset(b, 0, sizeof(b));
        int maxn = 10010, x, y;
        for(int i = 1; i <= n; i++) {
            maxn = 10010;
            for(int j = 1; j <= n; j++) {
                scanf("%d", &arr[i][j]);
                if(arr[i][j] != -1){
                    maxn = min(maxn, arr[i][j]);
                }
                if(arr[i][j] == -1) {
                    x = i;
                    y = j;
                }
            }
            a[i] = maxn;
        }
        int maxm;
        for(int i = 1; i <= n; i++) {
            int maxm = 10010;
            for(int j = 1; j <= n; j++) {
                if(arr[j][i] != -1){
                    maxm = min(maxm, arr[j][i]);
                }
            }
            b[i] = maxm;
        }
        if(x == 1 && y == 1)cout << (a[x] + b[y]) - max((a[x+1]+b[y] - arr[x+1][y]),(a[x]+b[y+1]-arr[x][y+1])) << endl;
        else if(x==n && y==1)cout << (a[x] + b[y]) - max((a[x-1]+b[y] - arr[x-1][y]),(a[x]+b[y+1]-arr[x][y+1])) << endl;
        else if(x==1&&y==n)cout << (a[x] + b[y]) - max((a[x]+b[y-1] - arr[x][y-1]),(a[x+1]+b[y]-arr[x+1][y])) << endl;
        else if(x==n&&y==n)cout << (a[x] + b[y]) - max((a[x-1]+b[y] - arr[x-1][y]),(a[x]+b[y-1]-arr[x][y-1])) << endl;
        else if(x==1)cout << (a[x] + b[y]) - (a[x+1]+b[y]-arr[x+1][y]) << endl;
        else if(x==n)cout << (a[x] + b[y]) - (a[x-1]+b[y]-arr[x+1][y]) << endl;
        else cout << (a[x] + b[y]) - (a[x+1]+b[y] - arr[x+1][y]) << endl;
    }
    return 0;
}

D: Growth

时间限制: 1 Sec  内存限制: 256 MB

题目描述

弱弱有两个属性ab,这两个属性初始的时候均为0,每一天他可以通过努力,让a涨1点或b涨1点。

为了激励弱弱努力学习,我们共有n种奖励,第i种奖励有xiyizi三种属性,若axibyi,则弱弱在接下来的每一天都可以得到zi的分数。

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

输入

第一行一个两个整数nm(1 ≤ n ≤ 1000,1 ≤ m ≤ 2000000000)。

接下来n行,每行三个整数xiyizi(1 ≤ xi, yi ≤ 1000000000,1 ≤ zi ≤ 1000000)。

输出

一行一个整数表示答案。

样例输入

2 4
2 1 10
1 2 20

样例输出

50

提示


在样例中,弱弱可以这样规划:第一天a涨1,第二天b涨1,第三天b涨1,第四天a涨1。


共获得0 + 0 + 20 + 30 = 50分。

Orz wls。。。 

把奖励的x拿出来从小到大排序,得到x1,x2,...,xn。

把奖励的y拿出来从小到大排序,得到y1,y2,...,yn。

 

用v[i][j]表示a值到达xi,b值达到yi时接下来每天可以得到的奖励。

 

v[i][j] = v[i - 1][j] + v[i][j - 1] - v[i - 1][j - 1] + t[i][j]

其中t[i][j]为满足x=i,y=j的奖励的总和。

 

用f[i][j]表示a值达到xi,b值达到yj时已经拿到的奖励的最大值。

 

f[i][j] + (x[i + 1] - x[i] - 1) * t[i][j] + t[i + 1][j] -> f[i + 1][j]

f[i][j] + (y[j + 1] - y[j] - 1) * t[i][j] + t[i][j + 1] -> f[i][j + 1]

最后统计一下答案就可以了。

不过这题有一个离散化不好写,我也是借鉴大佬的写法,

#include<bits/stdc++.h>
using namespace std;

#define ios1 ios::sync_with_stdio(0)
#define ios2 cin.tie(0)
#define ll long long
#define inf 0x7ffffffff
typedef pair<int, int> P;
map<P, int>mp;
set<int>sx, sy;

const int maxn = 1200;

int n, m;
int xx[maxn], yy[maxn];
ll dp[maxn][maxn], v[maxn][maxn];
int x[maxn], y[maxn], z[maxn];

int main() {
    ios1; ios2;
    while(cin >> n >> m) {
        int xi = 1, yi = 1;
        for(int i = 0; i < n; i++) {
            cin >> x[i] >> y[i] >> z[i];
            if(mp.count(P(x[i], y[i])) == 0)mp[P(x[i], y[i])] = z[i];
            else mp[P(x[i], y[i])] += z[i];
            if(sx.count(x[i]) == 0){sx.insert(x[i]);xx[xi++] = x[i];}
            if(sy.count(y[i]) == 0){sy.insert(y[i]);yy[yi++] = y[i];}
        }
        sort(xx, xx + xi);
        sort(yy, yy + yi);//离散化
        memset(dp, 0, sizeof(dp));
        memset(v, 0, sizeof(v));
        for(int i = 1; i < xi; i++) {
            for(int j = 1; j < yi; j++) {
                if(mp.count(P(xx[i], yy[j])) == 0)v[i][j] = v[i-1][j] + v[i][j-1] - v[i-1][j-1];
                else v[i][j] = v[i-1][j] + v[i][j-1] - v[i-1][j-1] + mp[P(xx[i], yy[j])];
            }
        }
        for(int i = 0; i < xi; i++) {
            for(int j = 0; j < yi; j++) {
                dp[i+1][j] = max(dp[i+1][j], dp[i][j] + (xx[i+1] - xx[i] - 1) * v[i][j] + v[i+1][j]);
                dp[i][j+1] = max(dp[i][j+1], dp[i][j] + (yy[j+1] - yy[j] - 1) * v[i][j] + v[i][j+1]);
            }
        }
        ll ans = 0;
        for(int i = 1; i < xi; i++) {
            for(int j = 1; j < yi; j++) {
                ll r = dp[i][j] + (m - xx[i] - yy[j]) * v[i][j];
                ans = max(ans, r);
            }
        }
        cout << ans << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_37444209/article/details/81454110
今日推荐