您的位置:首页 > 其它

codeforces contest 352

2016-12-02 14:24 288 查看
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
;
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
;
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
,b
,d

;
int n,m;
struct Mat {
ll mat

;
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]
[j];
}
}
printf("%I64d\n",powmul(M,m).mat[0][0]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: