3.3-3.10 1. NIM游戲 百度鏈接:https://baike.baidu.com/item/Nim%E6%B8%B8%E6%88%8F/6737105?fr=aladdin
定義:
P局面的所有子局面都是N局面。N局面的子局面中必有一個是P局面
性質:\(a_1 \ xor \ a_2 \ xor \cdots xor \ a_n = 0\) 則為P局面
證明:
若某個局面異或結果大于0,則一定存在一個操作使得\(a_1 \ xor \ a_2 \ xor \cdots xor \ a_n = 0\)
若某個局面異或結果等于0,則一定不存在一個操作使得\(a_1 \ xor \ a_2 \ xor \cdots xor \ a_n = 0\)
因為若Nim和為X,X得二進制表示最左邊的1在第k位,則一定存在一個該位為1的堆。設這堆火柴數量為Y,則只需把它拿成 Z = Y xor X 根火柴,得到的狀態就是必敗狀態。
2. chomp!游戲 m*n的棋盤,每次取走一個方格并拿掉它右邊和上面的所有方格。拿到(1,1)的人輸。
先手必勝,因為先手可以在后手先一步達到必勝狀態。
3. SG函數 SG(x) = mex(S)
4. 動歸復習 1. LCIS 求兩個序列的最長公共上升子序列
d[i][j]表示A的前i個與B的前j個,并且以\(B_j\) 為結尾的LCIS長度。為啥一定要帶這個結尾信息呢?因為進行下一次拼接時,必須知道結尾信息,否則不能滿足上升關系。
A[i] != B[j]時,d[i][j] = d[i-1][j] A[i] == B[j]時,\(d[i][j] = max_{\{1\le k<j, B_k<A_i\}} {d[i-1][k]}+1\) #include <bits/stdc++.h>
using namespace std;
int n;
int A[3030],B[3030];
int d[3030][3030];
int main(){cin>>n;for(int i=1;i<=n;i++)scanf("%d",&A[i]);for(int i=1;i<=n;i++)scanf("%d",&B[i]);int ans = 0;for(int i=1;i<=n;i++){int val = 0;for(int j=1;j<=n;j++){if(B[j] == A[i]){d[i][j] = val+1;}else d[i][j] = d[i-1][j];if(B[j]<A[i]) val = max(val,d[i-1][j]);ans = max(ans,d[i][j]);}}cout<<ans<<endl;return 0;
}
2. POJ-3666 $S = |A_1?-?B_1| + |A_2?-?B_2| + ... + |A_N?-?B_N?| $
給出A數組,求一個不下降或者不上升的B數組,使得S值最小
可以猜到的是,B數組中的所有元素都在A中出現過。(數學歸納法可以證明,但也要靠直覺)
然后既然A來自B,那么也就是說可以先把A復制一份,然后從中選某些數字,進而得到B。
\(d[i][j]\) 表示A數組的前 i 個,當以\(B_j\) 結尾時,S的最小值
\(d[i][j] = min_{1\le k\le j} d[i-1][k] + abs(A[i]-B[j])\)
其中B必須先離散化,隨著j有序之后才可以。
ll a[2010],b[2010],d[2010];
int n;
ll abs(ll a){return a>0?a:-a;
}
ll dp()
{for(int i=1;i<=n;i++)d[i] = abs(a[1]-b[i]);for(int i=2;i<=n;i++){ll mi = d[1];for(int j=1;j<=n;j++){mi = min(mi,d[j]);d[j] = mi + abs(a[i]-b[j]);}}ll ans = d[1];for(int i=2;i<=n;i++)ans = min(ans,d[i]);return ans;
}
bool cmp(ll a,ll b)
{return a>b;
}
int main()
{cin>>n;for(int i=1;i<=n;i++){scanf("%lld",&a[i]);b[i] = a[i];}sort(b+1,b+1+n);ll ans = dp();sort(b+1,b+1+n,cmp);ans = min(ans,dp());cout<<ans<<endl;return 0;
}
3. CH-5102 Mobile Service 三個員工在1,2,3。
要求按照p序列,必須有一個員工到達對應位置。求最少距離。要求一個地方不能有兩個員工。
很容易想到$d[i][x][y][z] $ 表示當前為p[i],三個員工為x,y,z位置時的最少花費。
但是該算法規模為 \(1000*200^3\) 故不能承受。
但是考慮到 i 時刻,必然有一個員工在p[i]位置。所以可以節約一維。另外轉移的時候一定要注意,不能有兩個人在同一個地方
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int cost[220][220];
int L,n,p[1010];
int d[1010][220][220];
int main(){cin>>L>>n;for(int i=1;i<=L;i++)for(int j=1;j<=L;j++)scanf("%d",&cost[i][j]);for(int i=1;i<=n;i++)scanf("%d",&p[i]);p[0] = 1;memset(d,0x3f,sizeof d);d[0][2][3] = 0;for(int i=0;i<n;i++){for(int j=1;j<=L;j++){for(int k=1;k<=L;k++){if(p[i+1]!=j&&p[i+1]!=k)d[i+1][j][k] = min(d[i+1][j][k],d[i][j][k] + cost[p[i]][p[i+1]]);if(p[i+1]!=k&&p[i+1]!=p[i])d[i+1][p[i]][k] = min(d[i+1][p[i]][k],d[i][j][k] + cost[j][p[i+1]]);if(p[i+1]!=j&&p[i+1]!=p[i])d[i+1][j][p[i]] = min(d[i+1][j][p[i]],d[i][j][k] + cost[k][p[i+1]]);}}}int res = inf;for(int i=1;i<=L;i++)for(int j=1;j<=L;j++){if(i!=p[n]&&j!=p[n])res = min(res,d[n][i][j]);}cout<<res<<endl;return 0;
}
4. CH-5103 傳紙條 d[i][x1][x2] 表示走了 i 步,一條路徑在x1,一條路徑在x2時的最大權值和
#include <bits/stdc++.h>
using namespace std;
int n,m;
int a[55][55];
int d[110][55][55];
int main(){cin>>n>>m;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);d[0][1][1] = a[1][1];for(int i=0;i<n+m-2;i++){for(int x1 = 1;x1<=1+i&&x1<=n;x1++){for(int x2 = x1;x2<=1+i&&x2<=n;x2++){int y1 = i+2-x1;int y2 = i+2-x2;if(y1>m||y2>m)continue;d[i+1][x1][x2+1] = max(d[i+1][x1][x2+1],d[i][x1][x2]+a[x1][y1+1]+a[x2+1][y2]);if(x1==x2){d[i+1][x1][x2] = max(d[i+1][x1][x2],d[i][x1][x2]+a[x1][y1+1]);d[i+1][x1+1][x2+1] = max(d[i+1][x1+1][x2+1],d[i][x1][x2]+a[x1+1][y1]);}else{d[i+1][x1][x2] = max(d[i+1][x1][x2],d[i][x1][x2] + a[x1][y1+1]+a[x2][y2+1]);d[i+1][x1+1][x2+1] = max(d[i+1][x1+1][x2+1],d[i][x1][x2]+a[x1+1][y1]+a[x2+1][y2]);if(x1==x2-1){d[i+1][x1+1][x2] = max(d[i+1][x1+1][x2],d[i][x1][x2]+a[x1+1][y1]);}else d[i+1][x1+1][x2] = max(d[i+1][x1+1][x2],d[i][x1][x2]+a[x1+1][y1]+a[x2][y2+1]);}}}}cout<<d[n+m-2][n][n]<<endl;return 0;
}
5. CF-1118 D2 - Coffee and Coursework (Hard Version) #include <bits/stdc++.h>
using namespace std;
const int N = 200010;
typedef long long ll;
int n,m;
ll a[N],b[N];
bool cmp(ll a,ll b){return a>b;}
bool check(int mid){ll sum = 0;for(int i=0;;i++){if(mid+i*mid<=n&&a[mid+i*mid]>=i){sum += b[(i+1)*mid] - b[i*mid] - i*(mid);}else{int index;for(index = i*mid+1;index<=n&&index<=(i+1)*mid;index++)if(a[index]<=i)break;sum += b[index-1]-b[i*mid]-i*(index - i*mid -1);break;}}return sum>=m;
}
int main(){cin>>n>>m;for(int i=1;i<=n;i++)scanf("%lld",&a[i]);sort(a+1,a+n+1,cmp);for(int i=1;i<=n;i++){b[i] = b[i-1]+a[i];}int l = 1,r = n;while(l<r){int mid = (l+r)/2;if(check(mid))r = mid;else l = mid+1;}if(check(r))cout<<r<<endl;else cout<<-1<<endl;return 0;
}
6. CF-1121 C - System Testing 煩人的模擬,一開始理解錯題意了,看題這種事情實在是不能馬虎 #include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int n,m;
int now[110],a[1010],pre[110],now_id[110],has[1010];
int round(int x,int y){return (int)((double)x*100/y+0.5);
}
int main(){cin>>n>>m;for(int i=1;i<=n;i++)scanf("%d",&a[i]);priority_queue< pair<int,int> > q;int num = 0;for(int i=1;i<=m;i++){now[i] = a[i];pre[i] = 1;now_id[i] = i;q.push(make_pair(-a[i],i));}int now_time = 0;int res = 0,t = m;for(int now_time = 1;now_time<=150*1000;now_time++){for(int i=1;i<=m;i++){if(now[i]<now_time){num++;if(t<n){now[i] += a[++t];pre[i] = now_time;now_id[i] = t;}else{now[i] = inf;pre[i] = inf;}}}int rou = round(num,n);for(int i=1;i<=m;i++){if(now_time-pre[i]+1==rou){has[now_id[i]] = 1;}}if(num == n)break;}for(int i=1;i<=n;i++)if(has[i])res++;cout<<res<<endl;return 0;
}
7. CF-1036 C - Classy Numbers 組合數 尋思了挺久,不過1A。大概還是用到了部分遞推。函數寫了一堆 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll L,R;
int T;
ll C[20][20];
ll A[20][20];
ll po[20];
//初始化組合數和10的n次方
void init(){C[0][0] = 1;for(int i=1;i<=18;i++){C[i][0] = 1;for(int j=1;j<=i;j++){C[i][j] = C[i-1][j-1] + C[i-1][j];}}po[0] = 1;for(int i=1;i<=18;i++)po[i] = po[i-1]*10;
}
//計算某個數長度
int calc_len(ll x){int len = 0;while(x){x/=10;len++;}return len;
}
//計算某個數的頭位有多大
int calc_left(ll x){while(x>=10)x/=10;return x;
}
ll calc(ll x,ll num){if(num==0)return 1;ll len = calc_len(x);ll max_left = calc_left(x);//printf("%lld %lld %lld %lld\n",x,len,max_left,num);ll res = 0;for(int i=num-1;i>=0;i--){res += (max_left-1)*C[len-1][i]*A[9][i];}for(int i=num;i>=0;i--)res += C[len-1][i]*A[9][i];//printf("%lld %lld\n",x,res);res += calc(x-max_left*po[len-1],num-1);return res;
}
int main(){init();A[9][0] = 1;A[9][1] = 9;A[9][2] = 81;A[9][3] = 729;cin>>T;while(T--){cin>>L>>R;//printf("calc(R,3):%lld,calc(L,3):%lld\n",calc(R,3),calc(L,3));ll res = calc(R,3)-calc(L-1,3);printf("%lld\n",res);}return 0;
}
方法二:先把所有滿足要求的數字求出來,然后二分劃定區間。可以求出這樣的數字不是特別多 $910^2 A_{18}^3 = 4406400 $ #include <bits/stdc++.h>#define forn(i, n) for (int i = 0; i < int(n); i++)using namespace std;vector<long long> res;
//可以說非常巧妙了
void brute(int pos, int cnt, long long cur){if (pos == 18){res.push_back(cur);return;}brute(pos + 1, cnt, cur * 10);if (cnt < 3)for (int i = 1; i <= 9; ++i)brute(pos + 1, cnt + 1, cur * 10 + i);
}int main() {brute(0, 0, 0);res.push_back(1000000000000000000);int T;scanf("%d", &T);forn(i, T){long long L, R;scanf("%lld%lld", &L, &R);printf("%d\n", int(upper_bound(res.begin(), res.end(), R) - lower_bound(res.begin(), res.end(), L)));}return 0;
}
8. CF-1132 C - Painting the Fence #include <bits/stdc++.h>
using namespace std;
int n,q;
int a[5010],b[5050],c[5050];
struct node{int l,r;
}k[5050];
bool cmp(node x,node y){if(b[x.r]-b[x.l-1]==b[y.r]-b[y.l-1]){return x.r-x.l<y.r-y.l;}return b[x.r]-b[x.l-1]<b[y.r]-b[y.l-1];
}
int main(){cin>>n>>q;for(int i=1;i<=q;i++){scanf("%d%d",&k[i].l,&k[i].r);a[k[i].l]++;a[k[i].r+1]--;}int res = 0;for(int i=1;i<=n;i++){a[i] += a[i-1];if(a[i]!=0)res++;b[i] += b[i-1] + (a[i]==1?1:0);c[i] += c[i-1] + (a[i]==2?1:0);//printf("%d ",c[i]);}int mi = 0x3f3f3f3f;for(int i=1;i<=q;i++){for(int j=i+1;j<=q;j++){int cnt = 0;int r = min(k[i].r,k[j].r);int l = max(k[i].l,k[j].l);cnt += b[k[i].r]-b[k[i].l-1] + b[k[j].r]-b[k[j].l-1];if(r>=l) cnt += c[r]-c[l-1];mi = min(mi,cnt);}}cout<<res-mi<<endl;return 0;
}
CF給的教程是枚舉每個 i ,然后統計出除 i 之外最優解。然后整體取最優解。 F - Clear the String #include <bits/stdc++.h>
using namespace std;
int n;
char s[550];
int d[550][550];
int main(){cin>>n;scanf("%s",s+1);memset(d,0x3f,sizeof d);for(int i=1;i<=n;i++)d[i][i] = 1;for(int i=1;i<=n;i++){for(int j=1;j<i;j++){for(int k=j;k<i;k++){if(k==i-1){if(s[k]==s[i])d[j][i] = min(d[j][i],d[j][k]);else d[j][i] = min(d[j][i],d[j][k]+1);continue;}if(s[k] == s[i]){d[j][i] = min(d[j][i],d[j][k]+d[k+1][i-1]);}else d[j][i] = min(d[j][i],d[j][k]+d[k+1][i-1]+1);}//printf("%d %d %d\n",j,i,d[j][i]);}}cout<<d[1][n]<<endl;return 0;
}
轉載于:https://www.cnblogs.com/1625--H/p/10499651.html
總結
以上是生活随笔 為你收集整理的3.3-3.9 周记 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。