NOIP 普及組 複賽 歷年 試題
NOIP 普及組 複賽 歷年 試題
NOIP 2016 普及組 複賽 試題
https://wenku.baidu.com/view/c317cd8abdeb19e8b8f67c1cfad6195f312be89f.html?rec_flag=default&mark_pay_doc=2&mark_rec_page=1&mark_rec_position=2&mark_rec=view_r_1&clear_uda_param=1可見試題
1.買鉛筆
https://www.luogu.org/problemnew/show/P1909可提交測評
//P1909 買鉛筆
//模擬樣例1
//57%2!=0 (57/2+1)*2=58
//57%50!=0 (57/50+1)*30=60
//57%30!=0 (57/30+1)*27=54
//該題是純模擬的水題,重心在整數的整除,取餘,找最值。
//有一個疑慮,int是否會溢位
//試算了一下,10000/1*10000=10^8 即需求10000只筆,每包1只,每隻花費10000,故總花費10000/1*10000=10^8,沒有溢位
//同時也確定了,min初始化要求min>10^8
//為了拿100分,分析還是挺關鍵的。
//樣例通過,信心滿滿,但還有些忐忑,提交,哇20個測試點,結果AC 2018-3-13
#include <stdio.h>
int main(){
int n,a,b,c,min=999999999,i;
scanf("%d",&n);
for(i=1;i<=3;i++){
scanf("%d%d",&a,&b);
if(n%a!=0)c=(n/a+1)*b;//不能整除
else c=n/a*b;//能整除
if(c<min)min=c;
}
printf("%d",min);
return 0;
}
2.迴文日期
https://www.luogu.org/problemnew/show/P2010可提交測評
//P2010 迴文日期
//看了日期表示方式,發現正是自己平時常用的表示方式,心定了,題目讀得進
//迴文,發現平時也有聯絡
//平年,閏年,平時也有寫過
//該題,將功能函式一個個編出,整合是關鍵
//思考如下,1將兩個日期之間所有整數取出,進行迴文判定,發現10^8,有超時的風險
//2將兩個日期之間的符合條件的日期取出,進行迴文判定,2000*365=7*10^5 不會超時
//寫得過程中,發現第一年,最後一年需特殊處理
//建議,編寫一個功能,測試一次,以防最後抓狂
//該題考點,分析出某個數的各個位置的值,字串簡單處理,整數取餘
//樣例通過,提交 AC。該題要求 程式程式碼 編寫要比較熟練。 2018-3-13
//該題編寫,新手耗時估計1小時
#include <stdio.h>
int y[3],m[3],d[3],a[3],c[]={0,31,0,31,30,31,30,31,31,30,31,30,31},cnt=0;
char s[10];
int date(int a[]){//根據 日期 解析出年 月 日 ,編的過程中,發現設想的難點:前導0沒有出現
int i;
for(i=0;i<2;i++){
y[i]=a[i]/10000,a[i]%=10000;
m[i]=a[i]/100,a[i]%=100;
d[i]=a[i];
}
}
int year(int a){//閏年判定,1 閏年 2 非閏年
if(a%100==0)//整百年處理
if(a%400==0)return 1;//閏年
else return 0;
else if(a%4==0)return 1;
else return 0;
}
int check(int a){//判斷迴文,1是,2否
int len=0,i;
while(a)s[len++]=a%10+'0',a/=10;//將日期解析成字串
for(i=0;i<len/2;i++)
if(s[i]!=s[len-1-i])return 0;
return 1;
}
int main(){
int i,j,k,b;//b新的日期
scanf("%d%d",&a[0],&a[1]);
date(a);
for(j=1;j<=12;j++){//生成 月 第一年處理
if(j==2)//2月單獨處理
if(year(y[0]))c[j]=29;//閏年
else c[j]=28;//非閏年
for(k=1;k<=c[j];k++){//生成 日
b=10000*y[0]+j*100+k;
if(check(b))cnt++;
}
}
for(i=y[0]+1;i<y[1];i++)//生成 月 日
for(j=1;j<=12;j++){//生成 月
if(j==2)//2月單獨處理
if(year(i))c[j]=29;//閏年
else c[j]=28;//非閏年
for(k=1;k<=c[j];k++){//生成 日
b=10000*i+j*100+k;
if(check(b))cnt++;
}
}
if(y[1]!=y[0])//如果沒有樣例1,這點容易忽略
for(j=1;j<=12;j++){//生成 月 最後一年處理
if(j==2)//2月單獨處理
if(year(y[1]))c[j]=29;//閏年
else c[j]=28;//非閏年
for(k=1;k<=c[j];k++){//生成 日
b=10000*y[1]+j*100+k;
if(check(b))cnt++;
}
}
printf("%d",cnt);
return 0;
}
3.海港
https://www.luogu.org/problemnew/show/P2058可提交測評
//P2058 海港
//看了一眼資料範圍,演算法複雜度不能是O(n^2),而O(nlogn) 比較合適
//題目配上說明,題意還是很清楚的
//看了題目的測試點,樸素演算法,還是能得70分
//仔細想想,該題屬於線性序列處理,適用的方法,連結串列,單調棧,單調佇列,樹狀陣列,線段樹
//再仔細想想,有單調性的資料,基本可用 單調佇列
// 1≤n≤10^5 ∑ki≤3*10^5 困惑比較大,如何處理,內容容易溢位
//P2058 海港
//突然間想到,用字首和更能優化
//q[100100] sum[1010]提交MLE
//q[10010] sum[1010]提交 70分,測試點7-11,13 RE 很滿意 2018-3-30 21:23
//以下程式碼為70分程式碼
//從70分跨越到100分,估計要改演算法,想不下去了,考慮看看題解
//https://blog.csdn.net/c20190102/article/details/53761648此文程式碼寫得不錯
//看了看,演算法的區別在於,應選人為研究物件,而不是選船為研究物件,不過,一開始沒想到,之後也確實想不到
//從上述角度,還是說明,演算法確實不同
//樣例通過,提交AC 2018-3-31 17:14
#include <stdio.h>
#include <string.h>
struct node{
int time;
int x;
}q[300100];//人
int h,t,vis[100100];
int main(){
int n,time,k,i,x,cnt=0;//此處寫成 int n,time,k,i,x,cnt; 忘記將cnt初始化
memset(vis,0,sizeof(vis));
scanf("%d",&n);
h=t=1;
while(n--){
scanf("%d%d",&time,&k);
while(h<t&&q[h].time+86400<=time){
vis[q[h].x]--;
if(!vis[q[h].x])cnt--;//像不像 尤拉圖 的計算
h++;
}
for(i=1;i<=k;i++){
scanf("%d",&x),q[t].time=time,q[t].x=x,t++;
if(!vis[x])cnt++;
vis[x]++;//有點像 尤拉圖 的計算
}
printf("%d\n",cnt);
}
return 0;
}
//以下程式碼為70分程式碼,僅供參考
//P2058 海港
//突然間想到,用字首和更能優化
//q[100100] sum[1010]提交MLE
//q[10010] sum[1010]提交 70分,測試點7-11,13 RE 很滿意 2018-3-30 21:23
//以下程式碼為70分程式碼
#include <stdio.h>
#include <string.h>
struct node{
int time;
int k;
int sum[1010];//此處開多少,沒有把握
}q[10010];
int h,t;
int main(){
int n,time,k,i,j,cnt;
memset(q,0,sizeof(q));
scanf("%d",&n);
h=t=1;
while(n--){
scanf("%d%d",&time,&k),q[t].k=k,q[t].time=time;
while(h<t&&q[h].time+86400<=time)h++;//此處寫成 while(h<t&&q[h].time+86400>=time)h++;查了好久
for(i=1;i<=k;i++)scanf("%d",&j),q[t].sum[j]=1;
for(j=1;j<=1000;j++)q[t].sum[j]+=q[t-1].sum[j];
t++;
cnt=0;
for(i=1;i<=1000;i++)
if(q[t-1].sum[i]-q[h-1].sum[i])cnt++;
printf("%d\n",cnt);
}
return 0;
}
4.魔法陣
https://www.luogu.org/problemnew/show/P2119可提交測評
//P2119 魔法陣
//https://blog.csdn.net/c20181503csy/article/details/53319238此文寫得比較粗
//https://blog.csdn.net/c20182030/article/details/53289443此文寫得比較細
//可兩篇文章,結合起來看
//需用到桶排序+排列組合知識
//樣例通過,提交AC 2018-4-5 12:54
#include <stdio.h>
#include <string.h>
#define maxn 15100
#define maxm 40100
#define LL long long
LL w[maxn],h[maxm],a[maxn],b[maxn],c[maxn],d[maxn];//h[i] i儲存物品序號 h[i]為物品魔法值 w[i]中i為魔法值,w[i]表示魔法值為i的物品個數 a[i]中i為魔法值 a[i]為該魔法值對應魔法陣的使用次數
LL n,m;
LL min(LL a,LL b){
return a<b?a:b;
}
LL max(LL a,LL b){
return a>b?a:b;
}
int main(){
LL i,j,x,y,min_n=16000,max_n=-1;
memset(w,0,sizeof(w));
scanf("%lld%lld",&n,&m);
for(i=1;i<=m;i++)scanf("%lld",&h[i]),w[h[i]]++,min_n=min(min_n,h[i]),max_n=max(max_n,h[i]);//此處做了優化,找出魔法值的最小值min_n,最大值max_n,之後整個程式執行時間縮短了一半
for(i=1;9*i<max_n-min_n;i++){//間距最小從1開始,自加,max_n-min_n是魔法值之間的最大間距
x=9*i+1,y=0;//x=9*i+1是a d之間的最小間距
for(j=min_n+9*i+1;j<=max_n;j++){//從D點開始列舉最小間距是9*i+1 因A B 在C D的左側 順序列舉
y+=w[j-x]*w[j-x+2*i];//計算a*b的組合 j-x 表示A物體的魔法值 j-x+2*i表示 B物體的魔法值 . 之前的a b影響之後的c d故,之前的a b組合數需累加
d[j]+=y*w[j-i];//d=a*b*c j-i表示 C物體的魔法值
c[j-i]+=y*w[j];//c=a*b*d j表示 D物體的魔法值
}
x=8*i+1,y=0;//注意:因C D在A B的右側,迴圈需逆序 列舉,順序不行
for(j=max_n-(9*i+1);j>=min_n;j--){//從A點開始列舉 順序列舉不行,即for(j=min_n;j<=max_n-(9*i+1);j++)是不可以的
y+=w[j+x]*w[j+x+i];//計算c*d的組合 j+x表示C物體的魔法值,j+x+i表示D物體的魔法值. 之前的c d影響之後的a b故,之前的c d組合數需累加
a[j]+=y*w[j+2*i];//j+2*i表示B物體的魔法值,
b[j+2*i]+=y*w[j];//j表示A物體的魔法值
}
}
for(i=1;i<=m;i++)printf("%lld %lld %lld %lld\n",a[h[i]],b[h[i]],c[h[i]],d[h[i]]);//此處寫成 for(i=1;i<=m;i++)printf("%d %d %d %d\n",a[h[i]],b[h[i]],c[h[i]],d[h[i]]);查了會
return 0;
}
//P2119 魔法陣
//試了 普及組的 深淺
//發現 沒有超出 範圍 的 未掌握的 知識點
//該題,若是比賽時,也沒想著拿滿分,只要有分拿,即可
//先快排
//開一個數組,儲存對應序號的順序,如q[i]=j,表示 自小到大 排在 第i位 元素 對應的 序號是 j 該思想意味著水平的大幅提升
//https://blog.csdn.net/ssl_zzy/article/details/53308193比較喜歡該文的寫法
//測試了一下,發現,樣例1,遺漏 物品1,3,7,6,其魔法值分別為1,7,26,29;
//經測試,發現7-1=6 26-7=19 19/3=6
//故,需要將除改成 乘
//即xb-xa<(xc-xb)/3 改成 3*(xb-xa)<(xc-xb)
//預測 四重 迴圈,能到測試點 7
//提交,發現,結果,不是WA ,就是TLE
//重新讀題,發現"保證標準輸出中的每個數都不會超過10^9","每行相鄰的兩個數之間用恰好一個空格隔開"
//果斷改成long long並按題目要求,改寫輸出格式
//提交,發現,結果,不是WA ,就是TLE
//if(x[j]-x[i]==2*(x[p]-x[k])&&3*(x[j]-x[i])<x[k]-x[j]&&x[i]<x[j]&&x[j]<x[k]&&x[k]<x[p])//此處寫成 if(x[j]-x[i]==2*(x[p]-x[k])&&3*(x[j]-x[i])<x[k]-x[j])確實忘了==情況的判定
//提交,測試點4-11 TLE 測試點12 TLE 得分55 2018-4-2 18:45
#include <stdio.h>
#include <string.h>
int q[40100],n,m;
long long x[40100],cnt[40100][5];
void quicksort(int left,int right){//自小到大
int i=left,j=right,t1;
long long mid=x[(left+right)/2],t2;
while(i<=j){
while(x[i]<mid)i++;
while(x[j]>mid)j--;
if(i<=j){
t2=x[i],x[i]=x[j],x[j]=t2;
t1=q[i],q[i]=q[j],q[j]=t1;
i++,j--;
}
}
if(left<j)quicksort(left,j);
if(i<right)quicksort(i,right);
}
int main(){
int i,j,k,p;
memset(cnt,0,sizeof(cnt));
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)scanf("%lld",&x[i]),q[i]=i;
quicksort(1,m);
for(i=1;i<=m;i++)
for(j=i+1;j<=m;j++)
for(k=j+1;k<=m;k++)
for(p=k+1;p<=m;p++)
if(x[j]-x[i]==2*(x[p]-x[k])&&3*(x[j]-x[i])<x[k]-x[j]&&x[i]<x[j]&&x[j]<x[k]&&x[k]<x[p])//此處寫成 if(x[j]-x[i]==2*(x[p]-x[k])&&3*(x[j]-x[i])<x[k]-x[j])確實忘了==情況的判定
cnt[q[i]][1]++,cnt[q[j]][2]++,cnt[q[k]][3]++,cnt[q[p]][4]++;
for(i=1;i<=m;i++){
for(j=1;j<=4;j++)
if(j==1)printf("%lld",cnt[i][j]);
else printf(" %lld",cnt[i][j]);
printf("\n");
}
return 0;
}
NOIP 2015 普及組 複賽 試題
https://wenku.baidu.com/view/33e7b2b252d380eb63946d2b.html可見試題
1.金幣
https://www.luogu.org/problemnew/show/P2669可提交測評
//P2669 金幣
//因k<=10000;每天收到10000枚,10000天,共計10000*10000=10^8,很明顯int夠用了
//並且,該題 純模擬 即可
//迴圈次數,1+2+3+...+n=(1+n)*n/2 n^2=10000極限n=100次
//樣例通過,
//自個編了幾組測試樣例,
//樣例
//1
//1
//樣例
//2
//3
//樣例
//3
//5
//樣例
//4
//8
//樣例
//5
//11
//樣例
//7
//18
//均順利通過,放心了,提交AC 2018-4-5 15:00
#include <stdio.h>
int main(){
int k,i=1,sum=0,cnt=0;
scanf("%d",&k);
while(1){
sum+=i*i;
cnt+=i;//天數
if(cnt>k)break;
i++;
}
cnt-=i,sum-=i*i;//回退,保證cnt<=k//多出天數的處理
sum+=(k-cnt)*i;//之後的第i天處理,天數是k-cnt,金幣,每天是i
printf("%d",sum);
}
2.掃雷遊戲
https://www.luogu.org/problemnew/show/P2670可提交測評
//P2670 掃雷遊戲
//題目,還沒有到廣度優先遍歷的難度
//具體就是列舉任意一點,若該點是地雷,就不做任何事,若是非地雷就列舉8個方向,統計地雷數
//有廣度優先遍歷的基礎,該題就是一道水題。
//樣例通過,提交AC 2018-4-5 15:33
//採用字串方式讀取雷區
#include <stdio.h>
char s[110][110];
int n,m,d[][2]={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}};//上 下 左 右 左上 右上 左下 右下
int main(){
int i,j,k,nr,nc,cnt;
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)scanf("%s",s[i]);
for(i=0;i<n;i++){//基本思路,邊處理 邊列印
for(j=0;j<m;j++)
if(s[i][j]=='*')printf("*");//地雷
else {
cnt=0;
for(k=0;k<8;k++){
nr=i+d[k][0],nc=j+d[k][1];//八個方向列舉
if(0<=nr&&nr<n&&0<=nc&&nc<m&&s[nr][nc]=='*')
cnt++;
}
printf("%d",cnt);
}
printf("\n");
}
return 0;
}
3.求和
https://www.luogu.org/problemnew/show/P2671可提交測評
//P2671 求和
//看了一樣10007基本可以確定是質數,簡單編碼做了檢驗,發現確實是質數
//檢驗程式碼如下
//看了資料範圍,O(n^2)超時,要麼O(n),要麼O(nlogn)
//有了NOIP 2016 普及組 魔法陣 的基礎後 該題就容易了
//看看int是否會溢位,極限情況取一個1 n/2 n 三元組 (1+100000)*(100000+100000)=2*10^10很明顯int溢位,採用long long
//樣例通過,看了看資料範圍,發現僅能通過前四組測試資料,得分40
//提交,確實如此,測試點2,6-10RE,得分40
//只按序號處理,40分,只按顏色處理,猜測60分 100000*20=2*10^6
//以下為40分程式碼
//測算了5組,6組 列舉20*20*100000/20=2*10^6剛好不超時
//只要同種原色間的差值是偶數,即為符合條件的x,z
//樣例通過,提交,測試點2,8-10 TLE ,果然60分,很是欣慰 2018-4-5 19:27
//以下為60分程式碼 與 40分 程式碼進行對比,確實是改演算法了
//100分,AC的演算法究竟長什麼樣。一窺真容,還是突然開竅。
//翻看了100分的演算法,發現,確實想不到,
//奇偶性,這個想法在60分程式碼裡有體現,不過,沒有深入下去 奇+奇=偶 奇+偶=奇 偶+偶=偶
//將表示式,乘開,動過想法,但發現沒什麼用,
//歸根結底,還是要多歷練。
//此文思路寫得不錯https://www.luogu.org/problemnew/solution/P2671
根據題目,我們可以設有三個下標,他們分別是x,y,z,要滿足x<y<z且y-x=z-y
所以我們可以模擬x,y,z,其時間複雜度為O(n^3),由於n<=100000所以一定會超時
但這個方法是可以優化的
由此我們可以得到2y=z+x
由此可知z+x必須是一個偶數即z,x同為奇數或同為偶數
所以我們其實只要窮舉x和z就行了,但這個方法的時間複雜度為O(n^2)所以還是會超時
我們可以用一下分組思想,把每個顏色分為一組,再在每個顏色中按奇偶分組,所以一共有2m組
設一個分組裡有k個數,這個分組中的數分別是x[1],x[2]……x[k],下標分別是y[1],y[2]……y[k]
那麼可得
答案=(x[1]+x[2])*(y[1]+y[2])+(x[1]+x[3])*(y[1]+y[3])+……+(x[1]+x[k])*(y[1]+y[k])
+(x[2]+x[3])*(y[2]+y[3])+(x[2]+x[4])*(y[2]+y[4])+……+(x[2]+x[k])*(y[2]+y[k])
+……
+(x[k-1]+x[k])*(y[k-1]+y[k])
=x[1]*(y[1]+y[2]+y[1]+y[3]+……+y[1]+y[k])
+x[2]*(y[1]+y[2]+y[2]+y[3]+……+y[2]+y[k])
+……
+x[k]*(y[1]+y[k]+y[2]+y[k]+……+y[k-1]+y[k])
=x[1]*(y[1]*(n-2)+y[1]+y[2]+……+y[k])
+x[2]*(y[2]*(n-2)+y[1]+y[2]+……+y[k])
+……
+x[k]*(y[k]*(n-2)+y[1]+y[2]+……+y[k])
然後事先將y[1]+y[2]+……+y[k]求出,用的時候呼叫就行了,其時間複雜度為O(n)
//樣例通過,提交AC 2018-4-6 9:51
#include <stdio.h>
#include <string.h>
#define maxn 100100
#define LL long long
#define mod 10007
LL n,m,number[maxn],color[maxn],sum[maxn][2],c[maxn][2];
int main(){
LL i,ans=0,y;
memset(sum,0,sizeof(sum)),memset(c,0,sizeof(c));
scanf("%lld%lld",&n,&m);
for(i=1;i<=n;i++)scanf("%lld",&number[i]);
for(i=1;i<=n;i++){
scanf("%lld",&color[i]);
c[color[i]][i%2]++;
sum[color[i]][i%2]=(sum[color[i]][i%2]+number[i])%mod;
}
for(i=1;i<=n;i++){
y=((c[color[i]][i%2]-2)*number[i]+sum[color[i]][i%2])%mod;
ans=(ans+i*y)%mod;//為了增加程式的可讀性,將拆成兩行來處理
}
printf("%lld",ans);
return 0;
}
//P2671 求和
//看了一樣10007基本可以確定是質數,簡單編碼做了檢驗,發現確實是質數
//檢驗程式碼如下
//看了資料範圍,O(n^2)超時,要麼O(n),要麼O(nlogn)
//有了NOIP 2016 普及組 魔法陣 的基礎後 該題就容易了
//看看int是否會溢位,極限情況取一個1 n/2 n 三元組 (1+100000)*(100000+100000)=2*10^10很明顯int溢位,採用long long
//樣例通過,看了看資料範圍,發現僅能通過前四組測試資料,得分40
//提交,確實如此,測試點2,6-10RE,得分40
//只按序號處理,40分,只按顏色處理,猜測60分 100000*20=2*10^6
//以下為40分程式碼
//測算了5組,6組 列舉20*20*100000/20=2*10^6剛好不超時
//只要同種原色間的差值是偶數,即為符合條件的x,z
//樣例通過,提交,測試點2,8-10 TLE ,果然60分,很是欣慰 2018-4-5 19:27
//以下為60分程式碼 與 40分 程式碼進行對比,確實是改演算法了
#include <stdio.h>
#define maxn 100100
#define LL long long
#define mod 10007
LL n,m,number[maxn],color[maxn][110];//對color[maxn][110]中的110惴惴不安,似乎應該開得更大,不過,容易MLE,color[i][j] 第i種顏色 對應的 第j個原色 對應的序號
int main(){
LL i,ans=0,x,z,j,k;
scanf("%lld%lld",&n,&m);
for(i=1;i<=n;i++)scanf("%lld",&number[i]),color[i][0]=0;
for(i=1;i<=n;i++)scanf("%lld",&k),color[k][++color[k][0]]=i;//用color[i][0]用來儲存對應的顏色的數量
for(i=1;i<=m;i++)
for(j=1;j<=color[i][0];j++)
for(k=j+1;k<=color[i][0];k++){
x=color[i][j],z=color[i][k];
if((z-x)%2==0)ans=(ans+(x+z)*(number[x]+number[z]))%10007;
}
printf("%lld",ans);
return 0;
}
//P2671 求和
//看了一樣10007基本可以確定是質數,簡單編碼做了檢驗,發現確實是質數
//檢驗程式碼如下
//看了資料範圍,O(n^2)超時,要麼O(n),要麼O(nlogn)
//有了NOIP 2016 普及組 魔法陣 的基礎後 該題就容易了
//看看int是否會溢位,極限情況取一個1 n/2 n 三元組 (1+100000)*(100000+100000)=2*10^10很明顯int溢位,採用long long
//樣例通過,看了看資料範圍,發現僅能通過前四組測試資料,得分40
//提交,確實如此,測試點2,6-10RE,得分40
//只按序號處理,40分,只按顏色處理,猜測60分 100000*20=2*10^6
//以下為40分程式碼
#include <stdio.h>
#define maxn 100100
#define LL long long
#define mod 10007
LL n,m,number[maxn],color[maxn];
int main(){
LL i,ans=0,x,z;
scanf("%lld%lld",&n,&m);
for(i=1;i<=n;i++)scanf("%lld",&number[i]);
for(i=1;i<=n;i++)scanf("%lld",&color[i]);
for(i=1;2*i<=n-1;i++)//i=y-x=z-y i是間距
for(x=n-2*i;x>=1;x--){
z=x+2*i;
if(color[x]==color[z]){
ans=(ans+(x+z)*(number[x]+number[z]))%10007;
}
}
printf("%lld",ans);
return 0;
}
//P2671 求和
//看了一樣10007基本可以確定是質數,簡單編碼做了檢驗,發現確實是質數
//檢驗程式碼如下
#include <stdio.h>
int main(){
int i;
for(i=2;i*i<=10007;i++)
if(10007%i==0)printf("i=%d",i);
return 0;
}
4.推銷員
https://www.luogu.org/problemnew/show/P2672可提交測評
//P2672 推銷員
//第一直覺是動態規劃
//如何處理距離,與推銷 疲勞
//仔細想想,無論走過多少戶,距離由最遠的決定
//f[i][j] 選j個數,在i個裡選
//資料範圍,2*10^8+10^3*10^5 int應該夠用
//拿出紙筆模擬,開始手忙腳亂,不過很快就適應了,該題對揹包要求理解深刻
//在模擬的過程中,發現需開兩個陣列 f[maxn][maxn],g[maxn][maxn];//f包括路徑的疲勞度 g不包括路徑的疲勞度
//樣例通過,估計能有60分
//儲存好上述程式碼,開始將二維陣列降為一維陣列
//按01揹包,一維陣列方式,順序迴圈,始終樣例未通過
//果斷按,完全揹包的方式,逆序迴圈,樣例通過,怎麼想到的?不知道,直覺告訴,說,可以這樣試試
//提交,測試點2,8-10TLE 還是60分,動歸到頭了
//一窺題解,
//此文程式碼寫得夠短https://www.luogu.org/problemnew/solution/P2672?&page=2思路摘抄如下
//這可能是一個最短的程式碼:
//看到這道題首先想到dp或貪心
//因為這是一道普及組的題,所以這題貪心就可以(滑稽)
//考慮我們如何將答案最大化:對於每個x,一定是選擇(一個最大的s)+(x-1個最大的a)或者x個最大的a,可以使答案最優
//那麼具體怎麼實現呢
//我們先把陣列按照a排序
//我們用sum[i]表示a陣列的前i項的和,q[i]表示s陣列的前i項的最大值,h[i]表示a[i]*2+s[i]後i項的最大值,對於每個x,他的答案就是max(sum[x]+2*q[x],sum[x-1]+h[x])
//讀完該題思路,以及程式碼,感覺真是神了,怎麼想到的呢,還是離不開樣例資料的模擬,觀察,以及大膽的猜想。
//樣例通過,提交AC 2018-4-7 17:14
#include <stdio.h>
#define maxn 100100
int s[maxn],a[maxn],sum_a[maxn],b[maxn],d[maxn],ans[maxn];//b[i]剩下部分最大的2*s[i]+a[i]
int max(int a,int b){
return a>b?a:b;
}
void quicksort(int left,int right){//自大到小排序
int i=left,j=right,mid=a[(left+right)/2],t;
while(i<=j){
while(a[i]>mid)i++;
while(a[j]<mid)j--;
if(i<=j){
t=a[i],a[i]=a[j],a[j]=t;
t=s[i],s[i]=s[j],s[j]=t;
i++,j--;
}
}
if(i<right)quicksort(i,right);
if(left<j)quicksort(left,j);
}
int main(){
int n,i;
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&s[i]);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
quicksort(1,n);
sum_a[0]=0,b[n+1]=0,d[0]=0;
for(i=1;i<=n;i++)sum_a[i]=sum_a[i-1]+a[i];//sum_a[i]前i個最大a的和
for(i=n;i>=1;i--)b[i]=max(b[i+1],2*s[i]+a[i]);
for(i=1;i<=n;i++)d[i]=max(d[i-1],s[i]);//1->i中的最遠距離
for(i=1;i<=n;i++)ans[i]=max(sum_a[i-1]+b[i],sum_a[i]+2*d[i]);
for(i=1;i<=n;i++)printf("%d\n",ans[i]);
return 0;
}
//P2672 推銷員
//第一直覺是動態規劃
//如何處理距離,與推銷 疲勞
//仔細想想,無論走過多少戶,距離由最遠的決定
//f[i][j] 選j個數,在i個裡選
//資料範圍,2*10^8+10^3*10^5 int應該夠用
//拿出紙筆模擬,開始手忙腳亂,不過很快就適應了,該題對揹包要求理解深刻
//在模擬的過程中,發現需開兩個陣列 f[maxn][maxn],g[maxn][maxn];//f包括路徑的疲勞度 g不包括路徑的疲勞度
//樣例通過,估計能有60分
//開始將二維陣列降為一維陣列
//在降維過程中,順便將二維陣列程式碼提交,確實60分,測試點2,8-10 RE 很是滿意。
//以下為60分程式碼,二維陣列的01揹包問題.
#include <stdio.h>
#include <string.h>
#define maxn 1100
int a[maxn],b[maxn],f[maxn][maxn],g[maxn][maxn];//f包括路徑的疲勞度 g不包括路徑的疲勞度
int main(){
int n,i,j;
memset(f,0,sizeof(f)),memset(g,0,sizeof(g));
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]);//a[i]距離
for(i=1;i<=n;i++)scanf("%d",&b[i]);//b[i]推銷疲勞度
for(i=1;i<=n;i++)//在i個物品裡選
for(j=1;j<=n;j++){//選j個物品
if(f[i-1][j]<g[i-1][j-1]+a[i]*2+b[i])
f[i][j]=g[i-1][j-1]+a[i]*2+b[i];
else f[i][j]=f[i-1][j];
if(g[i-1][j]<g[i-1][j-1]+b[i])
g[i][j]=g[i-1][j-1]+b[i];
else g[i][j]=g[i-1][j];
}
for(i=1;i<=n;i++)printf("%d\n",f[n][i]);
return 0;
}
//P2672 推銷員
//第一直覺是動態規劃
//如何處理距離,與推銷 疲勞
//仔細想想,無論走過多少戶,距離由最遠的決定
//f[i][j] 選j個數,在i個裡選
//資料範圍,2*10^8+10^3*10^5 int應該夠用
//拿出紙筆模擬,開始手忙腳亂,不過很快就適應了,該題對揹包要求理解深刻
//在模擬的過程中,發現需開兩個陣列 f[maxn][maxn],g[maxn][maxn];//f包括路徑的疲勞度 g不包括路徑的疲勞度
//樣例通過,估計能有60分
//儲存好上述程式碼,開始將二維陣列降為一維陣列
//按01揹包,一維陣列方式,順序迴圈,始終樣例未通過
//果斷按,完全揹包的方式,逆序迴圈,樣例通過,怎麼想到的?不知道,直覺告訴,說,可以這樣試試
//提交,測試點2,8-10TLE 還是60分,動歸到頭了
//一窺題解,
#include <stdio.h>
#include <string.h>
#define maxn 100100
int a[maxn],b[maxn],f[maxn],g[maxn];//f包括路徑的疲勞度 g不包括路徑的疲勞度
int max(int a,int b){
return a>b?a:b;
}
int main(){
int n,i,j;
memset(f,0,sizeof(f)),memset(g,0,sizeof(g));
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]);//a[i]距離
for(i=1;i<=n;i++)scanf("%d",&b[i]);//b[i]推銷疲勞度
for(i=1;i<=n;i++)//在i個物品裡選
for(j=n;j>=1;j--){//選j個物品
f[j]=max(f[j],g[j-1]+a[i]*2+b[i]);
g[j]=max(g[j],g[j-1]+b[i]);
}
for(i=1;i<=n;i++)printf("%d\n",f[i]);
return 0;
}
NOIP 2014 普及組 複賽 試題
https://wenku.baidu.com/view/8c054b6b844769eae109ed3b.html可見試題1.珠心算測驗
https://www.luogu.org/problemnew/show/P2141可提交測評
//P2141 珠心算測驗
//該題編寫方法很多
//第一步,將資料自小到大排序
//第二步,二重迴圈,計算和
//第三步,在資料裡查詢,是否有等於該和的數
//上述演算法,時間複雜度O(n^3),不會超時
//不過,覺得不舒服,立馬想到桶排序
//將第三步,開一個10000的陣列,和與數,直接在桶排序裡進行判別,時間複雜度,立馬變成O(n^2)
//10000+10000,計算數值不會超出int
//仔細想想,該題,一個桶排序,即可解決。
//樣例通過,提交,測試點2,4,6-10WA不敢想象
// 重讀一遍題目,發現"注意,加數和被加數必須是集合中的兩個不同的數。" ,說實話,該句有歧義
//是指 兩個數不相等,還是指集合中兩個不同位置的數
//重新讀題,發現"正整數集合,集合中的數各不相同"
//http://codevs.cn/problem/3459/也有該題,可檢視無法通過的資料
//看了測試資料,才發現,關鍵之處在於“其中有多少個數,恰好等於集合中另外兩個(不同的)數之和?”
//統計個數,不過,題意不明啊,考試時如何將這個意思讀出,真是很困難,
//將上述樣例給出,
//輸入:
100
6094 106 5958 877 3352 8959 292 9253 3860 448 5709 8295 4667 2588 3663 2486 1129 8870 3126 6203 727 4316 6541 9689 8818 2119 7331 7598 1879 7303 1100 8740 4567 2649 2373 7505 5848 4337 9953 4436 8395 8273 1028 4449 3363 6254 7856 6710 8422 3833 5690 6664 6326 2550 119 2075 3475 9024 3239 8889 807 543 8103 1441 9731 3873 5194 9557 3256 6245 3266 2777 4552 2390 8550 4777 9701 5694 9133 6736 2586 298 3695 1992 2958 3831 6905 8134 989 6292 8967 118 8442 1765 931 4760 5945 1703 6262 1671
//輸出:
22
//說明,如8889=4337+4552 8889=5194+3695 但,統計只能統計1次
//樣例通過,終於,提交AC 2018-4-7 22:19
//對該題的意見:如何能讓 被測試者 讀出題中意思,才是關鍵,該題難的不是程式碼,而是語義理解。
//個人認為,該題表述糟糕,難以讓人抓住重點。
#include <stdio.h>
#include <string.h>
int a[110],b[10100];
int main(){
int n,i,j,cnt=0;
memset(b,0,sizeof(b));
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]),b[a[i]]=1;
for(i=1;i<=n;i++)
for(j=i+1;j<=n;j++)
if(a[i]+a[j]<=10000&&b[a[i]+a[j]]==1)
cnt++,b[a[i]+a[j]]=0;
printf("%d",cnt);
return 0;
}
2.比例簡化
https://www.luogu.org/problemnew/show/P2118可提交測評
//看完題目,演算法就跳出來了
//列舉+歐幾里德演算法+浮點數計算(注意精度)
//樣例通過,提交,測試點4,7WA
//問題出在浮點數運算上。看來還是要定義一個0
//在http://codevs.cn/problem/3460/提交,看到了一組錯誤的資料
//輸入:
//199 2 100
//輸出:
//100 1
//else 歸誰管,遇到困惑, 測試下來結果是,與最近的if配對
//樣例通過,提交AC 2018-4-8 10:46
#include <stdio.h>
#include <math.h>
#define eps 1e-8
int gcd(int a,int b){//歐幾里德演算法
return b?gcd(b,a%b):a;
}
int main(){
int a,b,L,i,j,c,d;
double delta,min=999;
scanf("%d%d%d",&a,&b,&L);
for(i=1;i<=L;i++)
for(j=1;j<=L;j++)
if(gcd(i,j)==1){//互質
delta=fabs(i*1.0/j-a*1.0/b);//注意取絕對值,因浮點數計算存在誤差
if(delta>eps){//大於0
if(i*1.0/j-a*1.0/b>eps&&min>delta)//只處理A’/B’ ≥ A/B
min=delta,c=i,d=j;
}
else//<=0
if(min>delta)
min=delta,c=i,d=j;
}
printf("%d %d",c,d);
return 0;
}
3.螺旋矩陣
https://www.luogu.org/problemnew/show/P2239可提交測評
//P2239 螺旋矩陣
//第一想法,將矩陣格子填充完畢(類廣度優先遍歷的方式進行填充,發現高階演算法在普及組受限的比較多,基本都要超時),再進行查詢,演算法複雜度O(n^2),超時基本沒問題
//純列舉方式,只有50分,
//歸根結底,還是要找出公式
//畫圖,觀察,發現,每圈上元素個數為4*n-4 n>=2 ; 1 n=1
//每圈需2行2列,
//基本思路:看當前節點屬於矩陣的第幾圈,第幾個數,之後即可算出
//30000*30000=9*10^8 int不越界
//將矩陣一圈一圈拆除,同時注意加上拆除的元素個數
//樣例通過,並且測試了n=1,n=2,n=5 矩陣中的每個元素都測試通過,演算法時間複雜度O(n)
//提交AC,2018-4-8 18:17
#include <stdio.h>
int main(){
int n,r,c,cnt=0;
scanf("%d%d%d",&n,&r,&c);
while(1){
if(r==1||r==n||c==1||c==n){//在當前圈內 該花括號裡的計算過程,建議讀者畫圖理解
if(r==1)cnt+=c;
else if(r==n)cnt+=n+n-1+n-c;//n橫 n-1豎 n-c橫
else if(c==n)cnt+=n+r-1;
else if(c==1)cnt+=n+n-2+n+n-2-(r-2);
break;
}
cnt+=4*n-4,r--,c--,n-=2;//此處寫成n--//拆除該圈
}
printf("%d",cnt);
}
4.子矩陣
https://www.luogu.org/problemnew/show/P2258可提交測評
//P2258 子矩陣
//https://www.cnblogs.com/WQHui/p/5869500.html此文思路和程式碼都寫得不錯,摘抄如下:
//這題如果按暴力做只有一半分,最大時間複雜度為O(C(16,8)*C(16,8)); 很容易算出超時;
//我們可以發現如果直接dp會很難想,但是知道選哪幾行去dp就很好寫狀態轉移方程:
// dp[i][j]=min(dp[i][j],dp[k][j-1]+a[i]+b[k][i]);
//其中dp[i][j]表示 前i列裡選j列的子矩陣最小分值,注意,必需包含i列
// a[i]表示 第i列選到的行的總差值
// b[k][i]表示選到的每一行第k列和第i列的差值,注意,不包含k列與i列之間的列
//我們只要列舉 行 然後dp一次,取最小值即可 這樣最大時間複雜度就成了O(C(8,16)*n3);
//樣例通過,提交AC 2018-4-21 22:31
#include <stdio.h>
#include <string.h>
#define maxn 20
#define INF 999999999
int n,m,r,c,a[maxn][maxn],row[maxn],vis[maxn],min_sub=INF,zc[maxn],hc[maxn][maxn],f[maxn][maxn];//zc[i]縱差 即選定的r行中,第i列上,對應的行差的和 hc[i][j]即 選定的r行中 第i列 與 第j列 的 行差和
int abs(int x){
if(x<0)x=-x;
return x;
}
int min(int a,int b){
return a<b?a:b;
}
void dp(){
int i,j,k;
memset(zc,0,sizeof(zc)),memset(hc,0,sizeof(hc)),memset(f,0,sizeof(f));//f[i][j] 前i列中 選j列,包括第i列 對應的最小值
for(i=1;i<=m;i++)
for(j=1;j<r;j++)
zc[i]+=abs(a[row[j+1]][i]-a[row[j]][i]);
for(i=1;i<=m;i++)
for(j=i+1;j<=m;j++)
for(k=1;k<=r;k++)
hc[i][j]+=abs(a[row[k]][j]-a[row[k]][i]);
for(i=1;i<=m;i++)
for(j=1;j<=min(i,c);j++){//j<=min(i,c)表示 既要 j<=i 又要 j<=c
f[i][j]=INF;
for(k=j-1;k<=i-1;k++)
f[i][j]=min(f[i][j],f[k][j-1]+zc[i]+hc[k][i]);//此處寫成f[i][j]=min(f[i][j],f[k][j-1]+zc[j]+hc[k][j]);
}
for(i=c;i<=m;i++)
if(f[i][c]<min_sub)min_sub=f[i][c];
}
void dfs_row(int step){//列舉行
int i;
if(step==r+1){//選中r行
dp();
return ;
}
for(i=row[step-1]+1;i<=n;i++)
if(vis[i]==0){
vis[i]=1;
row[step]=i;
dfs_row(step+1);
vis[i]=0;
}
}
int main(){
int i,j;
scanf("%d%d%d%d",&n,&m,&r,&c);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
memset(vis,0,sizeof(vis)),row[0]=0;
dfs_row(1);//1表示第一步
printf("%d",min_sub);
return 0;
}
//P2258 子矩陣
//先試列舉,拿分是關鍵
//採用深搜,列舉行,之後列舉列
//P1008 三連擊 有該題基礎應該能拿些分數
//程式編好,樣例1,樣例2通過,估計能有50分,提交,55分,測試點3,5-11,13 TLE,很是滿意。2018-4-19
//以下程式碼為55分程式碼
//C(12,6)*C(12,6)*6*6=3.1*10^7 介於超時與不超時之間
//C(16,8)*C(16,8)*8*8=1.0*10^10 看來確實要超時了
//故 深搜+回溯 也就到頭了,要AC ,只能改演算法了。
#include <stdio.h>
#include <string.h>
int a[20][20],n,m,r,c,r2[20],c2[20],vis_r[20],vis_c[20],pre=1,b[20][20],score,min_sub=999999999;//b[][]存新陣列資料
void dfs_col(int step);//函式宣告
int abs(int x){
if(x<0)x=-x;
return x;
}
void dfs_row(int step){//列舉行
int i;
if(step==r+1){
dfs_col(1);
return ;
}
for(i=r2[step-1]+1;i<=n;i++)
if(vis_r[i]==0){
vis_r[i]=1;
r2[step]=i;
dfs_row(step+1);
vis_r[i]=0;
}
}
void dfs_col(int step){//列舉列
int i,j,k,sub=0;
if(step==c+1){
for(i=1;i<=r;i++)//將選出的行列對應節點資料放入新的矩陣,方便處理
for(j=1;j<=c;j++)
b[i][j]=a[r2[i]][c2[j]];
for(i=1;i<=r;i++)//行處理
for(j=1;j<c;j++)
sub+=abs(b[i][j+1]-b[i][j]);
for(j=1;j<=c;j++)
for(i=1;i<r;i++)//列處理
sub+=abs(b[i+1][j]-b[i][j]);
if(min_sub>sub)min_sub=sub;
return ;
}
for(i=c2[step-1]+1;i<=m;i++)//此處寫成for(i=1;i<=c;i++),昏了
if(vis_c[i]==0){
vis_c[i]=1;
c2[step]=i;
dfs_col(step+1);
vis_c[i]=0;
}
}
int main(){
int i,j,p,q;
memset(vis_r,0,sizeof(vis_r)),memset(vis_c,0,sizeof(vis_c)),r2[0]=0,c2[0]=0;
scanf("%d%d%d%d",&n,&m,&r,&c);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
dfs_row(1);
printf("%d",min_sub);
}
NOIP 2013 普及組 複賽 試題
https://wenku.baidu.com/view/aae243f1d1f34693daef3e6d.html?from=search可見試題
1.計數問題
https://www.luogu.org/problemnew/show/P1980可提交測評
//P1980 計數問題
//測算統計的數字個數 (1+1000000)*1000000/2=5*10^12
//每個數字,最多7個數,故統計範圍7*5*10^12=3.5*10^13 int溢位,需採用 long long
//樣例通過,提交AC 2018-4-22 9:05
//仔細想想,上述計算有誤,極限數字個數1000000,每個數字,最多7位數,故極限統計1000000*7=7*10^6故int 溢位
//馬上修改,提交AC 2018-4-22 9:55
#include <stdio.h>
int cnt=0,x;
void count(int n){//將n解析成一個一個的單個數字
while(n){
if(n%10==x)cnt++;
n/=10;
}
}
int main(){
int n,i;
scanf("%d%d",&n,&x);
for(i=1;i<=n;i++)
count(i);
printf("%d",cnt);
return 0;
}
2.表示式求值
https://www.luogu.org/problemnew/show/P1981可提交測評
//P1981 表示式求值
//"所有參與運算的數字均為 0 到 2^31-1 之間的整數" 粗想是採用long long
//再細細一想,每個數運算前,先對10000取模,再進行計算,那麼int 也就不會溢位了
//解析出數字,解析出運算子號,
//運算子100000,參與運算數字個數100000+1,每個數字最長位數10,故,字串長度(100000+1)*10+100000=1100001
//該題,說白了,就是字串處理
//遇到*先處理,第一遍掃描,處理完*運算
//之後,剩下的資料加加起來就好了。
//樣例通過,提交AC 2018-4-22 11:48
#include <stdio.h>
#include <string.h>
#define mod 10000
char s[1100100];
int a[110000];
int main(){
int i,len,k=0,flag=0,ans=0;//flag *標記 1 否則為0
memset(a,0,sizeof(a));
scanf("%s",s);
len=strlen(s);
for(i=0;i<len;i++)
if(s[i]=='+'){
if(flag==0)
k++;
else{
a[k-1]=a[k]%mod*(a[k-1]%mod)%mod;
a[k]=0;
flag=0;
}
}else if(s[i]=='*'){
if(flag==0){
flag=1;//漏了此句,查了會
k++;
}
else{
a[k-1]=a[k]%mod*(a[k-1]%mod)%mod;
a[k]=0;
flag=1;
}
}
else{
a[k]*=10;
a[k]+=s[i]-'0';
}
//字串掃描結束之後,還要對最後兩個數字進行判定
if(flag==1){
a[k-1]=a[k]%mod*(a[k-1]%mod)%mod;
a[k]=0;
}else//保證a[k]==0;
k++;
for(i=0;i<k;i++)//剩下的數,加加起來即可
ans=(ans+a[i])%mod;
printf("%d",ans);
return 0;
}
3.小朋友的數字
https://www.luogu.org/problemnew/show/P1982可提交測評
//該題,測試點資料有可能超越long long範疇,或者說long long有可能溢位,證明如下:
//提供一組測試資料給大家
//輸入:
5 981
-409 -401 97 -96 -301
//輸出:
-409
以上述資料為例
特徵值為
-409 -401 97 97 97
分數為
-409 -818 -818 -721 -624
//P1982 小朋友的數字
//讀題數十遍,發現,這句始終沒讀進"輸出時保持最大值的符號,將其絕對值對 p 取模後輸出。"
//不得不說,這句真的很難懂,很難懂"其它小朋友的分數為排在他前面的所有小朋友中(不包括他本人),小朋友分數加上其特徵值的最大值"
//以下程式碼為80分程式碼,2018-4-25 21:50
//https://blog.csdn.net/sunshinezff/article/details/45271411此文思路寫得不錯,摘抄如下:
主要考察語文能力,關鍵把題讀懂。
讀完題就能發現這是個(幾乎是裸的)最大子段和問題。對於最大子段和問題,我們有O(N)的演算法。 具體的做法是這樣的:當前要求第I位及之前的最大子段和,如果第(I-1)位及之前的最大子段和大於0,則顯然這一位取了也未嘗不可(不會減少),也就是當前這一位和前面一段連線起來。否則的話,就新開一段——把前面的最大子段和改成0以後繼續往下掃描。 如果一定要說這是DP也可以。
這樣樸素的做能得50分,
在計算特徵值與分數的過程中記錄一下最大值可以的得到80分
原因在與最後兩個點的分數值超過了longlong。
進一步分析可以發現除了第一個小朋友外剩下的小朋友的分數值是不下降的。所以對於一個小朋友他的分數只有兩種情況。
1.如果他的前一個小朋友的特徵值大於0,那他的分數就為前一個小朋友的分數加上前一個小朋友的特徵值。更新當前最大值。
2.如果他的前一個小朋友的特徵值小於0,那他的分數就為第二個小朋友的分數。
當一個小朋友的分數值大於1000000000時我們取模
因為第一個小朋友的分數不會大於1000000000,所以我們就可以在計算過程中判斷出來是否有小朋友的分數大於第一個小朋友。
這樣就可以拿到滿分。
//以下為AC程式碼,2018-4-25 22:48
#include <stdio.h>
#define LL long long
#define maxn 1000100
LL f[maxn],a[maxn],t[maxn],score[maxn];//t[i]為特徵值
LL max(LL a,LL b){
return a>b?a:b;
}
int main(){
LL n,p,i,MAX,ans,flag=0;//flag判斷是否後面的數大於score[1]
scanf("%lld%lld",&n,&p);
for(i=1;i<=n;i++)scanf("%lld",&a[i]);
t[1]=MAX=f[1]=a[1];//此處寫成t[i]=MAX=f[1]=a[1]; 查了好久
for(i=2;i<=n;i++){
f[i]=max(f[i-1]+a[i],a[i]);
MAX=max(MAX,f[i]);
t[i]=MAX;
}
score[1]=t[1],score[2]=t[1]+score[1];
for(i=3;i<=n;i++){
if(t[i-1]<=0)score[i]=score[i-1];//此處寫成 if(t[i]<=0)score[i]=score[i-1]; 查了好久好久,最後一處難查的錯誤
else score[i]=score[i-1]+t[i-1];//此處寫成 score[i]=score[i-1]+t[i]; 昏招
if(score[i]>1000000000)flag=1;//score[1]<=1000000000故 if(score[i]>1000000000)flag=1;
if(flag==1)score[i]%=p;
}
if(flag==1)ans=score[n];
else ans=score[1]>score[n]?score[1]:score[n];
printf("%lld",ans%p);
return 0;
}
//P1982 小朋友的數字
//讀題數十遍,發現,這句始終沒讀進"輸出時保持最大值的符號,將其絕對值對 p 取模後輸出。"
//不得不說,這句真的很難懂,很難懂"其它小朋友的分數為排在他前面的所有小朋友中(不包括他本人),小朋友分數加上其特徵值的最大值"
//以下程式碼為80分程式碼,2018-4-25 21:50
#include <stdio.h>
#define LL long long
#define maxn 1000100
LL f[maxn],a[maxn],t[maxn],score[maxn];//t[i]為特徵值
LL max(LL a,LL b){
return a>b?a:b;
}
int main(){
LL n,p,i,MAX,ans;
scanf("%lld%lld",&n,&p);
for(i=1;i<=n;i++)scanf("%lld",&a[i]);
t[1]=MAX=f[1]=a[1];//此處寫成t[i]=MAX=f[1]=a[1]; 查了好久
for(i=2;i<=n;i++){
f[i]=max(f[i-1]+a[i],a[i]);
MAX=max(MAX,f[i]);
t[i]=MAX;
}
score[1]=t[1],score[2]=t[1]+score[1];
for(i=3;i<=n;i++)score[i]=max(score[i-1]+t[i-1],score[i-1]);
ans=score[1]>score[n]?score[1]:score[n];
if(ans<0)printf("-%lld",-ans%p);
else printf("%lld",ans%p);
return 0;
}
//P1982 小朋友的數字
//"其它小朋友的分數為排在他前面的所有小朋友中(不包括他本人),小朋友分數加上其特徵值的最大值"該句難懂,藉助說明才搞懂
//"其他數字的絕對值均不超過 10^9"該句告訴我們,int不夠用了,要上long long
//1 ≤ n ≤ 1,000,000 演算法的時間複雜度僅限於O(n),O(nlogn),而O(n^2)已不適用
//對於 50%的資料,1 ≤ n ≤ 1,000 第一目標50分,基本的演算法,列舉,應能達成目標
//第一步,傻傻的算 b[i][j] i->j之間的和,發現演算法複雜度是O(n^3),程式碼如下
//for(i=1;i<=n;i++)
// for(j=1;j<=n;j++)
// for(k=i;k<=j;k++)
// b[i][j]+=a[k];
//立馬放棄,回憶起,學過字首和,是該用的時候了
//sum_max[i] 1->i中 最大的連續和 計算原理 如下:
//sum_max[1]: sum[1]=a[1]
//sum_max[2]: sum[1] sum[2] sum[2]-sum[1] 找最大值
//sum_max[3]: sum[1] sum[2] sum[2]-sum[1] sum[3] sum[3]-sum[1] sum[3]-sum[2]找最大值
//sum_max[4]: sum[1] sum[2] sum[2]-sum[1] sum[3] sum[3]-sum[1] sum[3]-sum[2] sum[4] sum[4]-sum[1] sum[4]-sum[2] sum[4]-sum[3]找最大值
//樣例通過,提交,測試點1 WA,測試點2,7-10 RE ,得分40,還算滿意 2018-4-23
//將 #define maxn 1100 改成 #define maxn 1000100 ,提交50分,測試點1 WA,測試點2,8-10 RE 很是滿意
//以下程式碼為50分程式碼
#include <stdio.h>
#include <string.h>
#define LL long long
#define maxn 1000100
#define INF 2000000000
LL a[maxn],sum[maxn],sum_max[maxn],score[maxn];//sum_max[i] 1->i中 最大的連續和
LL max(LL a,LL b){
return a>b?a:b;
}
int main(){
LL n,p,i,j,k,ans=-INF;
sum[0]=0;
scanf("%lld%lld",&n,&p);
for(i=1;i<=n;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
sum_max[1]=a[1];
for(i=2;i<=n;i++){//該功能是靠手動模擬得出的
sum_max[i]=sum_max[i-1];
for(j=0;j<i;j++)//此處寫成 for(j=1;j<i;j++)查了會,即漏了sum[i]的比較
sum_max[i]=max(sum_max[i],sum[i]-sum[j]);
}
score[1]=sum_max[1];
for(i=2;i<=n;i++)score[i]=max(score[i-1],score[i-1]+sum_max[i-1]);
for(i=1;i<=n;i++)ans=max(ans,score[i]);
printf("%lld",ans%p);
return 0;
}
4.車站分級
https://www.luogu.org/problemnew/show/P1983可提交測評
// P1983 車站分級
//模擬了資料,發現是圖,用到 入度 ,入度為0的 站點 屬同一級別,將其去除,新的入度為0的點屬於較低的一個級別,如此遞推
//看了資料範圍,鄰接矩陣 可用
//編寫的過程中,發現讀取資料,進行rd 計算時,程式會超時, 不過50分應該拿到了
//樣例通過,提交,測試點8 TLE,得分90,超乎想象,很滿意。2018-4-27
//以下程式碼為90分程式碼。
//該題需要AC,有一個至關重要的地方,若是採用鄰接矩陣,入度處理 應放在停靠站點上rd[a[j]]++,若放在非停靠站點上rd[k]++,該題始終無法AC,測試點8 始終TLE
//處理圖時,
for(k=a[1]+1;k<=a[si]-1;k++)
for(j=1;j<=si;j++)
if(vis[k]==0&&map[k][a[j]]==0)
map[k][a[j]]=1,rd[a[j]]++;
//寫成
for(k=a[1]+1;k<=a[si]-1;k++)
if(vis[k]==0)
for(j=1;j<=si;j++)
if(map[k][a[j]]==0)
map[k][a[j]]=1,rd[a[j]]++;
//僅是錦上添花,並未參與是否AC
//2018-4-27 19:00 AC 該題,發現上述問題,用了相當長的時間
//以下為AC程式碼
#include <stdio.h>
#include <string.h>
#define maxn 1100
int map[maxn][maxn],a[maxn],vis[maxn],rd[maxn],q[maxn];//q[i]佇列
int main(){
int n,m,i,j,si,k,cnt=0,h,t,x;
memset(map,0,sizeof(map)),memset(rd,0,sizeof(rd));
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){//讀取資料
memset(a,0,sizeof(a)),memset(vis,0,sizeof(vis));
scanf("%d",&si);
for(j=1;j<=si;j++)
scanf("%d",&a[j]),vis[a[j]]=1;//此處寫成 vis[j]=1 昏招
for(k=a[1]+1;k<=a[si]-1;k++)
if(vis[k]==0)
for(j=1;j<=si;j++)
if(map[k][a[j]]==0)
map[k][a[j]]=1,rd[a[j]]++;
}
memset(vis,0,sizeof(vis));//此句需寫在 迴圈while(1)之外
while(1){
h=t=1;
for(i=1;i<=n;i++)
if(vis[i]==0&&rd[i]==0)
vis[i]=1,q[t]=i,t++;
if(h==t)break;//無入度為0的點,跳出迴圈
while(h<t){
x=q[h];
for(i=1;i<=n;i++)//去除 入度為0 的點 對應的連線
if(map[x][i])
map[x][i]=0,rd[i]--;
h++;
}
cnt++;
}
printf("%d",cnt);
return 0;
}
//嘗試了鄰接表,發現 入度 選擇 非停靠的站 ,測試點8還是TLE
//無奈,還是將 入度 選擇 停靠的站 ,提交,耗時確實大幅降低。
//很遺憾,該題若 入度 選擇 非停靠的站 90分, 入度 選擇 停靠的站 100分
//考場上十全十美很難,有時缺憾也是一種美,90分,可以接受。2018-4-27 21:08
//以下程式碼為, 入度 選擇 停靠的站,鄰接表 AC 程式碼
#include <stdio.h>
#include <string.h>
#define maxn 1100
int map[maxn][maxn],a[maxn],vis[maxn],rd[maxn],q[maxn],head[maxn],cnt=0;//q[i]佇列
struct node{
int to;
int next;
}e[maxn*maxn/2];
void add(int u,int v){
cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
int main(){
int n,m,i,j,si,k,ans=0,h,t,x,b,v;
memset(map,0,sizeof(map)),memset(rd,0,sizeof(rd)),memset(head,0,sizeof(head));
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){//讀取資料
memset(a,0,sizeof(a)),memset(vis,0,sizeof(vis));
scanf("%d",&si);
for(j=1;j<=si;j++)
scanf("%d",&a[j]),vis[a[j]]=1;//此處寫成 vis[j]=1 昏招
for(k=a[1]+1;k<=a[si]-1;k++)
if(vis[k]==0)
for(j=1;j<=si;j++)
if(map[k][a[j]]==0)
map[k][a[j]]=1,add(k,a[j]),rd[a[j]]++;
}
memset(vis,0,sizeof(vis));//此句需寫在 迴圈while(1)之外
while(1){
h=t=1;
for(i=1;i<=n;i++)
if(vis[i]==0&&rd[i]==0)
vis[i]=1,q[t]=i,t++;
if(h==t)break;//無入度為0的點,跳出迴圈
while(h<t){
x=q[h];
b=head[x];
while(b){
v=e[b].to;
rd[v]--;
b=e[b].next;
}
h++;
}
ans++;
}
printf("%d",ans);
return 0;
}
// P1983 車站分級
//以下為採用 鄰接表+記憶化搜尋 AC程式碼,很遺憾對 入度 選取有要求,停靠站點計算入度(codevs,洛谷)均能AC;非停靠站點計算入度,(codevs無法AC 測試點7 TLE)洛谷能AC。
//2018-4-28 17:30 AC
#include <stdio.h>
#include <string.h>
#define maxn 1100
int map[maxn][maxn],a[maxn],vis[maxn],rd[maxn],head[maxn],cnt=0,d[maxn];//d[i] i點深度
struct node{
int to;
int next;
}e[maxn*maxn/2];
void add(int u,int v){
cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
int max(int a,int b){
return a>b?a:b;
}
int dfs(int u){
int b,v;
if(d[u]){//記憶化搜尋
return d[u];
}
d[u]=1,b=head[u];
while(b){
v=e[b].to;
d[u]=max(d[u],dfs(v)+1);
b=e[b].next;
}
return d[u];
}
int main(){
int n,m,i,j,si,k,ans=0,h,t,x,b,v;
memset(map,0,sizeof(map)),memset(rd,0,sizeof(rd)),memset(head,0,sizeof(head)),memset(d,0,sizeof(d));
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){//讀取資料
memset(a,0,sizeof(a)),memset(vis,0,sizeof(vis));
scanf("%d",&si);
for(j=1;j<=si;j++)
scanf("%d",&a[j]),vis[a[j]]=1;//此處寫成 vis[j]=1 昏招
for(k=a[1]+1;k<=a[si]-1;k++)
if(vis[k]==0)
for(j=1;j<=si;j++)
if(map[k][a[j]]==0)
map[k][a[j]]=1,add(k,a[j]),rd[a[j]]++;
}
for(i=1;i<=n;i++)
if(rd[i]==0)
ans=max(ans,dfs(i));
printf("%d",ans);
return 0;
}
// P1983 車站分級
//模擬了資料,發現是圖,用到 入度 ,入度為0的 站點 屬同一級別,將其去除,新的入度為0的點屬於較低的一個級別,如此遞推
//看了資料範圍,鄰接矩陣 可用
//編寫的過程中,發現讀取資料,進行rd 計算時,程式會超時, 不過50分應該拿到了
//樣例通過,提交,測試點8 TLE,得分90,超乎想象,很滿意。2018-4-27
//以下程式碼為90分程式碼。
//看了些AC程式碼,發現本人資料讀取這塊還是寫得挺漂亮的,TLE 問題出在 一層一層的 處理 入度為0的點上
//看了些AC程式碼,採用棧 ,發現是 開 兩個棧,這樣效率確實高,觸類旁通,開兩個佇列
//修改,提交,測試點8,還是TLE
//以下程式碼仍為90分程式碼。2018-4-27
#include <stdio.h>
#include <string.h>
#define maxn 1100
int map[maxn][maxn],a[maxn],vis[maxn],rd[maxn],q1[maxn],q2[maxn];//q[i]佇列
int main(){
int n,m,i,j,si,k,cnt=0,h1,t1,x,h2,t2;
memset(map,0,sizeof(map)),memset(rd,0,sizeof(rd));
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){//讀取資料
memset(vis,0,sizeof(vis));
scanf("%d",&si);
for(j=1;j<=si;j++)