题目来源:清北学堂
天天寄快递
express.in/.out/.cpp
【问题描述】
天天暑假时帮别⼈寄送快递,经历了⼀个暑假,天天积累了不少数据,
想对快递公司进⾏⼀下评分,得到快递公司的质量⽔平。
总共有 n 家快递公司,编号为 1..n。现在天天有 m 天的寄送快递数据,
其中第 i 天使⽤第 e i 家快递公司,快递在路上花了 t i 天时间。⼀开始每个
快递公司的评分都为 0,对于⼀家快递公司,如果⼀个包裹花了 t i 天寄到,
那么对这家快递公司的评分贡献为 2−t i , (如果花的时间超过两天得分就会
变成负的啦) 。
然⽽事情没有这么简单,如果某⼀天的数据丢掉了,天天为了公平起见
就忽略掉这天的数据。于是快递公司联盟决定雇佣⼀个⼩偷,⼩偷可以偷⾛
最多 s 天的数据,使得每个公司的信⽤得分⾄少增加 k,且所有快递公司的
信⽤总和尽量⼤。
若如果被偷以后,⽆法让每个公司的信⽤得分都⾄少增加 k,输出
−23333333,否则请你输出被偷后, 所有快递公司的信⽤得分的和最多增
加多少。
【输入格式】
第⼀⾏四个整数 n,m,s,k,其中 1 ≤ n,m ≤ 100,000,0 ≤ s ≤ m,0 ≤
k ≤ 10 9 。
接下来 m ⾏,每⾏两个整数。接下来的第 i ⾏为 e i ,t i ,其中 1 ≤ e i ≤
n,0 ≤ t i ≤ 10 9 。
【输出格式】
⼀个整数,为题⽬要求的答案。
2
【样例输入】
2 5 4 22
1 1
1 40
2 25
2 30
2 0
【样例输出】
89
【样例解释】
⼩偷可以偷 4 天的数据,但是⼩偷实际上只偷了第 2,3,4 天的数据,1
号公司获得了 38 分的提升,2 号公司获得了 23 + 28 分的提升,都满⾜了
最⼩提升 22 分的要求。
【数据规模和约定】
对于 30% 的数据,1 ≤ n,m ≤ 20。
对于 100% 的数据,1 ≤ n,m ≤ 200,000,。
题解:贪心
代码:
考试30分....就是正解呀....靠 我知道了数组开小了,应该200005 嘤嘤嘤..改了就A 了...
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; #define maxn 100005 int n,m,s,k,tot; int v[maxn]; long long ans; inline int read(){ char ch=getchar();int x=0,f=1; for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0'; return x*f; } struct WW{ int id,ad,has; }w[maxn]; bool cmp(WW a,WW b){ return a.ad>b.ad; } bool check(){ for(int i=1;i<=n;i++)if(v[i]<k)return 0; return 1; } int main(){ freopen("express.in","r",stdin); freopen("express.out","w",stdout); n=read();m=read();s=read();k=read(); //n家快递,m个数据,s天,增加k。 for(int i=1;i<=m;i++){ w[i].id=read(); int x; x=read(); w[i].ad=x-2; } sort(w+1,w+m+1,cmp); for(int i=1;i<=m;i++){ int id=w[i].id; if(w[i].ad<0)continue; if(v[id]<k){ v[id]+=w[i].ad; ans+=w[i].ad; w[i].has=1; tot++; } } if(!check()||tot>s){ printf("-23333333\n"); fclose(stdin); fclose(stdout); return 0; } sort(w+1,w+m+1,cmp); if(tot<s){ for(int i=1;i<=m;i++){ if(w[i].has||w[i].ad<0)continue; ans+=w[i].ad;tot++; if(tot==s)break; } } cout<<ans<<endl; fclose(stdin); fclose(stdout); return 0; }
正解:贪心
局部贪心:为了使每个公司都增加K,每次选可增加次数最多的,一旦增加到了K,就停止。
全局贪心:为了使总和最多,如果还能选每次在剩下选最大的
如果偷了s天仍没有使每个公司增加K,无解
代码:
唉...可能是考试使时间不多了敲得很慌张...按原来思路敲一遍就A了....
清北的题真不错.....
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 200005 using namespace std; int n,m,s,k,day; int v[maxn]; long long ans; struct WW{ int id,ad,has; }w[maxn]; inline int read(){ char ch=getchar();int x=0,f=1; for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0'; return x*f; } bool cmp(WW a,WW b){ return a.ad>b.ad; } bool check(){ for(int i=1;i<=n;i++)if(v[i]<k)return false; return true; } int main(){ n=read();m=read();s=read();k=read(); //n个公司,m个数据,偷s天,每个增加k for(int i=1;i<=m;i++){ int x; w[i].id=read(); x=read(); w[i].ad=x-2; } sort(w+1,w+m+1,cmp); for(int i=1;i<=m;i++){ int ad=w[i].ad,id=w[i].id; if(ad<0)continue; if(v[id]<k){ day++; v[id]+=ad; w[i].has=1; ans+=ad; if(day==s)break; } } if(!check()){ printf("-23333333\n"); return 0; } if(day<s){ sort(w+1,w+m+1,cmp); for(int i=1;i<=m;i++){ if(w[i].has||w[i].ad<0)continue; ans+=w[i].ad; day++; if(day==s)break; } } cout<<ans<<endl; return 0; }
天天和不可描述
unknown.in/.out/.cpp
【问题描述】
天天和 () 是好朋友,然⽽总是唱反调。对于⼀个有 () 的字符串,天天
总是会把 () 内的所有东西都倒过来读。
⽐如对于字符串 abc(def) ,天天看到的就是 abcfed 。
括号⾥⾯可能是空的, 也有可能套有多个括号, ⽐如说 abc(hello)(world)lcy()x(owq(zrt))
,天天看到的就是 abcollehdlrowlcyxzrtqwo 。
因为 (owq(zrt)) ⾸先变成了 (trz)qwo ,接下来变成了 zrtqwo ,zrt 被
反转了两次,所以天天看到的还是 zrt。
现在给你⼀个字符串,问你天天看到的是什么样⼦的?
【输入格式】
⼀⾏⼀个字符串,表⽰原始的字符串。保证字符串内只有⼩写字母和 (
以及) 组成。保证括号总是配对的。
【输出格式】
⼀⾏⼀个字符串,表⽰天天看到的字符串。
【样例输入 1】
abc(hello)(world)lcy()x(owq(zrt))
【样例输出 1】
abcollehdlrowlcyxzrtqwo
【样例输入 2】
(y(g(el)da)nis)
4
【样例输出 2】
singleday
【样例输入 3】
mian()
【样例输出 3】
mian
【数据规模和约定】
对于 10% 的数据,保证只出现⼀对括号,字符串长度⼩于 100,000。
对于另外 30% 的数据,保证字符串长度⼩于 100。
对于另外 40% 的数据,保证字符串长度⼩于 100,000。
对于剩余的 20% 的数据,保证字符串长度⼩于 500,000。
题解:
40分比赛代码...辣鸡... 咦?我去评测怎么80分.....T了2个点
统计每个括号的左右位置,stl里的函数进行从右到左翻转,从右到左是为了避免括号套括号混乱的情况
不过看这个得分好像没什么卵用。
//35分钟finish reverse 只能对字符数组操作 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 500005 using namespace std; int js,top; int vec[maxn]; char s[maxn]; struct WW{ int l,r; }w[maxn]; bool cmp(WW a,WW b){ return a.l>b.l; } int main(){ freopen("unknown.in","r",stdin); freopen("unknown.out","w",stdout); cin>>s; int len=strlen(s); for(int i=0;i<len;i++){ if(s[i]=='('){ vec[++top]=i; } if(s[i]==')'){ w[++js].l=vec[top--]; w[js].r=i; } } sort(w+1,w+js+1,cmp); for(int i=1;i<=js;i++){ int l=w[i].l,r=w[i].r; reverse(s+l,s+r+1); } for(int i=0;i<len;i++) if(s[i]>='a'&&s[i]<='z')printf("%c",s[i]); fclose(stdin); fclose(stdout); return 0; }
罪犯分组
prison.in/.out/.cpp
【问题描述】
B 城有1座监狱,1共关押着 N 名罪犯,编号分别为 1-N。
他们的关系十分不和谐。很多罪犯之间甚至积怨已久,如果客观条件具
备则随时可能爆发冲突。
在详细考察了 N 名罪犯间的⽭盾关系后,警察局长发现罪犯之间的⽭
盾关系可以⽤⼀个 N 个点 M 条边的⽆向图来表⽰:如果 x 到 y 有⼀条边,
表⽰罪犯 x 和罪犯 y 有⽭盾。
现在警察局长要把这些罪犯分成⼀些⼩组,每名罪犯属于且仅属于⼀个
⼩组。
为了开展活动顺利,要求每个⼩组内最多有 K 对罪犯有⽭盾,同时为
了管理⽅便,警察局长希望最小化分成的⼩组数量。
那么,应如何分配罪犯,才能使分成的⼩组数量最少?这个最⼩值是多
少?
【输入格式】
第3 个整数,N,M,K,意义见上述。
接下来 M 行,每行两个数字 x,y。表示x 和 y 之间有⽭盾。
【输出格式】
输出一行一个个值,表示最少的分组数量。
【样例输入】
3 3 1
1 2
2 3
1 3
6
【样例输出】
2
【数据规模和约定】
对于每个测试点,N 分别等于 2,4,6,8,10,12,14,15,16,16。
0 ≤ M,K ≤
N(N−1)
2
保证是1个无自环、无重边的无向图。
题解:
莫名其妙乱搞60分...受宠若惊...
乱合并..能一个房间就一个房间...顺便记录每个房间有多少个仇家。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; vector<int>vec[22]; int n,m,k,ans; int fa[22],d[22][22],tmp[22],v[22]; int f(int x){ return fa[x]==x?x:fa[x]=f(fa[x]); } void change(int fx,int fy){ for(int i=0;i<vec[fx].size();i++){ int p=vec[fx][i]; tmp[i]=p; } for(int i=0;i<vec[fy].size();i++){ int p=vec[fy][i]; vec[fx].push_back(p); } for(int i=0;i<vec[fx].size();i++){ vec[fy].push_back(tmp[i]); } return; } int main(){ freopen("prison.in","r",stdin); freopen("prison.out","w",stdout); scanf("%d%d%d",&n,&m,&k); ans=n; for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); d[x][y]=d[y][x]=1; } for(int i=1;i<=n;i++)fa[i]=i,vec[i].push_back(i); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ int fx=f(i),fy=f(j); if(fx==fy)continue; int sum=0; for(int p=0;p<vec[fx].size();p++){ int g=vec[fx][p]; if(d[j][g]&&g!=i)sum++; } for(int p=0;p<vec[fy].size();p++){ int g=vec[fy][p]; if(d[i][g])sum++; } if(sum+v[fx]+v[fy]<=k){ fa[fx]=fy; v[fy]+=sum+v[fx]; ans--; change(fx,fy); if(ans==1){ printf("1\n"); fclose(stdin); fclose(stdout); return 0; } break; } } } printf("%d\n",ans); fclose(stdin); fclose(stdout); return 0; }