得分30(正解写炸)+30(暴力很稳)+50(玄学)
本次考试三道题感觉都不是按照正常NOIP题出的,个人感觉三道题难度差距并没有太大,还好今天除了T1都还比较稳。
T1:就是一个贪心,当到第三张牌及其以后的时候每次判一下是对子,还是与前面的两张组成顺子更优,而且每次留一张牌,是的后面的选择具有更多的可能性。当然你也可以DP,其实思想是一样的。
T2:一道结论题,大佬们都说和今年的初赛题很像,然后就一眼看出结论,作为一名蒟蒻+学渣,安安心心打完暴力就走,也没有浪费太多时间。但对于这些结论题,不能仅仅是看结果找规律,还要结合题意合理分析,毕竟不是所有题都是什么递推一下就行了,当没有思路时,可以往gcd,最小公倍数方面想一想,说不定就想出来了。
T3:网络流 OR 匈牙利 直接弃疗,一波排序+贪心搞了50分还是可以了,下来学了一下匈牙利发现还是挺简单的,想学的点这里
T1:
题意:给你一堆牌,每次可以将两张一样的作为对子打出,也可以将三张连续的牌作为顺子打出,求对子和顺子的总数最多是多少。
题解:通过科学分析我们可以找到一种贪心的方法,对于前两张牌,直接组成对子,3-n每次先判断能不能与前两张牌组成顺子,然后再组成对子
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 1000005;
int n,x,mx,ans,a[N];
inline int Readint(){
int i=0,f=1;char ch;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
return i*f;
}
int main(){
// freopen("poke.in","r",stdin);
// freopen("poke.out","w",stdout);
n=Readint();
for(int i=1;i<=n;i++) x=Readint(),a[x]++,mx=max(mx,x);
for(int i=1;i<=mx;i++){
ans+=a[i]/2;
a[i]%=2;
if(i+1if(a[i]==1 && a[i+1]%2 && a[i+2]){
ans++;
a[i]--;
a[i+1]--;
a[i+2]--;
}
}
}
cout<return 0;
}
T2:
题意:一个球,在一个边长为n和m的地板上弹,每个地砖的长度为1,刚开始有左上角向右下方运动,每次到边界就反弹,求到每个瓷砖会给它染色,让你求刚好被染一次色的瓷砖个数。
题解:我们将 n - - .m - -,让每个瓷砖变成格子图上的一个点,通过分析我们发现,小球走过路径是 n*m/gcd(n,m)+1,这时候我们只需要将染过两次色的地砖减即可,我们由画图可以发现,染过两次色的瓷砖要不在一个叉叉的中心,要不在四个叉叉的交界处,由此我们可以计算结果了。
#include
using namespace std;
long long T,n,m;
inline long long Readint(){
long long i=0,f=1;char ch;
for(ch=getchar();(ch<'0'||ch>'9');ch=getchar());
for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
return i*f;
}
inline long long gcd(long long a,long long b){
return (b==0 ? a : gcd(b,a%b));
}
inline long long solve(long long x,long long y){
x--;y--;
long long k=gcd(x,y);
long long tot=1LL*x*y/k+1; //求出总路径
long long t=2*k;
long long twice=((x/t)*1LL*(y/t)+(x/t-(x%t==0?1:0))*1LL*(y/t-(y%t==0?1:0))); //求出算过两次的瓷砖个数
return tot-2*twice;
}
int main(){
T=Readint();
while(T--){
n=Readint(),m=Readint();
printf("%I64d\n",solve(n,m));
}
return 0;
}
T3:
题意:给你一些不能旋转的盒子,有长为L,宽为W,每次可以将一个盒子放进一个长宽都不小于它的盒子(此过程可以一直重复),求最后留下的面积最小。
题解:我们可以先将每个盒子按面积从高到低排序,然后对于每个盒子,向每个长宽都大于等于它的盒子连一条边,这时候我们可以贪心,从面积大的盒子开始求最大匹配,最后结果就是我们所求的。我们可以先将所有面积加上,每次匹配成功就减去一个面积
当然也可以求最小费用流
#include
using namespace std;
int n,ans,belong[205];
bool line[205][205],vis[205];
struct node{
int x,y;
}box[205];
bool cmp(const node&a,const node&b){
return a.x*a.y>b.x*b.y;
}
inline int Readint(){
int i=0,f=1;char ch;
for(ch=getchar();(ch<'0'||ch>'9');ch=getchar());
for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
return i*f;
}
inline bool find(int x){
for(int i=1;iif(line[x][i]==true && !vis[i]){
vis[i]=true;
if(!belong[i] || find(belong[i])){
belong[i]=x;
return true;
}
}
}
return false;
}
int main(){
// freopen("box.in","r",stdin);
n=Readint();
for(int i=1;i<=n;i++){
box[i].x=Readint();
box[i].y=Readint();
}
sort(box+1,box+1+n,cmp);
for(int i=1;i<=n;i++){
ans+=box[i].x*box[i].y;
for(int j=i+1;j<=n;j++){
if(box[i].x>=box[j].x&&box[i].y>=box[j].y)
line[j][i]=true;
}
}
for(int i=2;i<=n;i++){
memset(vis,false,sizeof(vis));
if(find(i)) ans-=box[i].x*box[i].y;
}
cout<return 0;
}