codeforces contest 352

http://codeforces.com/contest/352

//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

C 351A Jeff and Rounding

题意:给出2n个数,将其中n个向下取整,剩下n个向上取整,要使操作之后的数之和和操作之前的数之和的差值最小,问最小差值。

题解:很明显只和小数部分有关,那么我们把有小数部分的数的小数部分提取出来。排序,选择前k小向下取整,维护一个最小的ans。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define de(x) cout << #x << "=" << x << endl
const int N=4005;
double a[N];
int main() {
	int n;
	while(~scanf("%d",&n)) {
		int m=0;
		double x,det=0;
		for(int i=1;i<=2*n;++i) {
			cin>>x;
			int xx=x;
			if(x-(double)xx>0.0001) {
				a[++m]=x-(double)xx;
				det+=x-(double)xx;//加上实数部分 
			}
		}
		sort(a+1,a+1+m);
		double ans=fabs(det-(m-max(0,m-n)));
		int tt=min(m,n);
		for(int i=max(0,m-n)+1;i<=tt;++i) {//i个向下取整的 
			double t=fabs(det-(m-i));
			ans=fmin(ans,t);
		}
		printf("%.3lf\n",ans);
	}
    return 0;
}








//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

D  351B  Jeff and Furik
题意:
题解:其实我是猜的(刷完概率dp专题再来写题解)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define de(x) cout << #x << "=" << x << endl
const int N=3005;
int a[N];
int main() {
	int n;
	while(~scanf("%d",&n)) {
		int cnt=0; 
		for(int i=1;i<=n;++i) {
			scanf("%d",&a[i]);
			for(int j=1;j<i;++j) {
				if(a[j]>a[i]) ++cnt;
			}
		}
		if(cnt==0) {
			puts("0.000000");
		} else if(cnt&1) {
			printf("%d.000000\n",2*cnt-1);
		} else {
			printf("%d.000000\n",2*cnt);
		}
	}
    return 0;
}





//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

E  351C  Jeff and Brackets
题意:要构造一个n*m的合法括号序列。如果位置i是左括号,那么它的值为a[i%n],否则 它的值为b[i%n]。求最小的值。
题解:
1、定义bal:左括号数量减去右括号数量。
2、把待求序列ANS拆分成m段大小为n的序列。可以证明ANS的 前缀bal ≤ n(证明下面写)。
3、我们取出第X段。
d[k][i][j](d[0~n][1~n][0~n])表示前X-1段序列的 前缀bal = k , 前X-1段序列加上第X段序列的前i个字符的 前缀bal = j 。
d[k][0][k]=0
if(j-1≥0)  d[k][i][j]=min(d[k][i][j],d[k][i-1][j-1]+a[i]);
if(j+1≤n) d[k][i][j]=min(d[k][i][j],d[k][i-1][j+1]+b[i]);
4、因此我们可以得出一个矩阵M。
M[i][j]表示 前X-1段序列的前缀bal=i,如果要让 前X段序列的前缀bal= j,需要花费的代价。
5、很容易知道第0段的前缀bal是0,现在我们要求的是:让前m段的前缀bal=0花费的最小代价。
这个可以用矩阵快速幂优化。(比较不好描述,可以看矩阵快速幂部分的代码)
最后的答案就是powmul(M,k).mat[0][0]
证明ANS的 前缀bal ≤ n:
取出一段前缀bal大于n(假设它的值是res)的序列A,那么它长度必定大于n,并且在它之后必定有一段序列B的bal等于-res,序列B的长度也大于n。
在维护序列的合法性的情况下,我们必定可以把A中的一部分和B中的一部分交换,使得res的值下降。重复这个步骤直到ANS的前缀bal≤n。得证。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define de(x) cout << #x << "=" << x << endl
const int N=25;
ll a[N],b[N],d[N][N][N];
int n,m;
struct Mat {
    ll mat[N][N];
    Mat() {memset(mat,0x3f,sizeof(mat));}
    Mat operator * (Mat B) {
        Mat C;
        for(int i=0;i<=n;++i) {
            for(int j=0;j<=n;++j) {
                for(int k=0;k<=n;++k) {
                    C.mat[i][j]=min(C.mat[i][j],mat[i][k]+B.mat[k][j]);
                }
            }
        }
        return C;
    }
};
Mat powmul(Mat A,int k) {
    Mat B;
    for(int i=0;i<=n;++i) B.mat[i][i]=0;
    while(k) {
        if(k&1) B=B*A;
        A=A*A;
        k>>=1;
    }
    return B;
}
Mat M;
int main() {
	while(~scanf("%d%d",&n,&m)) {
		for(int i=1;i<=n;++i) scanf("%I64d",&a[i]);
		for(int i=1;i<=n;++i) scanf("%I64d",&b[i]);
        memset(d,0x3f,sizeof(d));
        for(int k=0;k<=n;++k) {
            d[k][0][k]=0;
            for(int i=1;i<=n;++i) {
                for(int j=0;j<=n;++j) {
                    if(j-1>=0)
                    d[k][i][j]=min(d[k][i][j],d[k][i-1][j-1]+a[i]);
                    if(j+1<=n)
                    d[k][i][j]=min(d[k][i][j],d[k][i-1][j+1]+b[i]);
                }
            }
        }
        for(int i=0;i<=n;++i) {
            for(int j=0;j<=n;++j) {
                M.mat[i][j]=d[i][n][j];
            }
        }
        printf("%I64d\n",powmul(M,m).mat[0][0]);
	}
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值