https://ac.nowcoder.com/acm/contest/882/F
F题 Partition Problem
题意:有两个队,n 个人,每个人和其他 n-1 有个竞技值,当处于不同队时,就能增加这个值,求最大的总竞技值。
分析:暴力DFS回溯搞组合数。
对于这个问题,就得先构造一个初始状态入手,初始状态也就是全都在B队,由于关键在于对立,所以可以让1先在A队来简化,接下来的入A队就加上该加的竞技值,减去该减的竞技值。
然后每个都用 for 不断往下搜索,记录vis[ ] 和 cnt,如果cnt 满了就比较结果,并退一步把当前位扔掉,然后继续往下搜索(八皇后)。
#includeusing namespace std; typedef long long ll; const int maxn = 30; int val[maxn][maxn]; bool in[maxn]; int n,m; ll ans=0; void dfs(int now,int cnt,ll sum){ if(cnt==n){ ans = max(ans,sum); return; } for(int i=now; i<=n+cnt+1; i++){ //这里写n+cnt+1比写m快 if(!in[i]){ ll tmp=sum; for(int j=1; j<=m; j++){ if(in[j]) sum -= val[i][j]; //这里是i 而我一开始错写成了+=val[now][j],WA了好久 else sum += val[i][j]; } in[i] = true; dfs(i+1,cnt+1,sum); in[i] = false; //回溯 sum = tmp; } } } inline int read(){ int res = 0, w = 0; char ch = 0; while(!isdigit(ch)){ w |= ch == '-', ch = getchar(); } while(isdigit(ch)){ res = (res << 3) + (res << 1) + (ch ^ 48); ch = getchar(); } return w ? -res : res; } int main(){ scanf("%d",&n); m = n<<1; for(int i=1; i<=m; i++){ for(int j=1; j<=m; j++){ val[i][j] = read(); } } ll sum; sum = ans = 0; in[1] = true; //先构造个初始状态,所有人都在红队,再把1放进白队 for(int i=2; i<=m; i++){ sum += val[1][i]; } dfs(2,1,sum); printf("%lld\n", ans); return 0; }
https://ac.nowcoder.com/acm/contest/882/H
H题 Second Large Ractangle
悬线法求极大子矩阵的裸题。 相关资料:https://www.cnblogs.com/-Zzz-/p/11503639.html给由01组成的矩阵,问只包含1的子矩阵第二大的面积是多少。
由于这个预处理方法会导致一整块的L,R,U的值都同化,所以还要处理出这一块的(L-1)*H 和 L*(H-1)。
还要注意 g[i][j] 等于 0 时不算数,不要拿去算面积
更新ans1时要用大于等于号
#includeusing namespace std; const int maxn = 1e3+300; int g[maxn][maxn],Left[maxn][maxn],Right[maxn][maxn],Up[maxn][maxn]; char ch[maxn]; int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1; i<=n; i++){ scanf("%s",ch+1); for(int j=1; j<=m; j++){ g[i][j] = ch[j]-'0'; Up[i][j] = 1; Left[i][j] = Right[i][j] = j; } } for(int i=1; i<=n; i++){ for(int j=2; j<=m; j++){ if(g[i][j]==1 && g[i][j-1]==1){ Left[i][j] = Left[i][j-1]; } } } for(int i=1; i<=n; i++){ for(int j=m-1; j>=1; j--){ if(g[i][j]==1 && g[i][j+1]==1){ Right[i][j] = Right[i][j+1]; } } } int maxh = 0,maxl = 0; int L,R,U,ans1=0,ans2=0; for(int i=1; i<=n; i++){ for(int j=1; j<=m; j++){ if(i>1 && g[i][j]==1 && g[i-1][j]==1){ Up[i][j] = Up[i-1][j]+1; Left[i][j] = max(Left[i-1][j], Left[i][j]); Right[i][j] = min(Right[i-1][j], Right[i][j]); //悬线法 } if(ans1<= (Right[i][j] - Left[i][j] + 1)*Up[i][j] ){ R = Right[i][j], L = Left[i][j], U = Up[i][j]; maxl = Right[i][j] - Left[i][j] + 1; // printf("l=%d\n", maxl); maxh = Up[i][j]; // printf("h=%d\n", maxh); ans1 = maxl*maxh; } } } ans2 = max(ans2,(maxl-1)*maxh); ans2 = max(ans2,maxl*(maxh-1)); for(int i=1; i<=n; i++){ for(int j=1; j<=m; j++){ if(g[i][j]==0) continue; if(Right[i][j] == R && Left[i][j]==L && Up[i][j]==U) continue; ans2 = max( ans2,(Right[i][j] - Left[i][j] + 1)*Up[i][j] ); } } printf("%d\n", ans2); } // #include // #include // using namespace std; // const int Max = 2005; // int ves[Max][Max], up[Max][Max], Left[Max][Max], Right[Max][Max]; // int temp1 = 1, temp2 = 1; // int main(void) // { // ios::sync_with_stdio(false); // int N, M; // cin >> N >> M; // for(int i = 1; i <= N; i++) // for (int j = 1; j <= M; j++) { // cin >> ves[i][j]; // Left[i][j] = Right[i][j] = j; //初始化Right和Left,使他们值为点所在纵坐标 // up[i][j] = 1; //初始化up使其值为1 // } // for (int i = 1; i <= N; i++) // for (int j = 2; j <= M; j++) // if (ves[i][j] == 1 - ves[i][j - 1]) //判断相邻两个数是否不同 // Left[i][j] = Left[i][j - 1]; //是,则 // for (int i = 1; i <= N; i++) // for (int j = M - 1; j > 0; j--) // if (ves[i][j] == 1 - ves[i][j + 1]) // Right[i][j] = Right[i][j + 1]; // for(int i = 1;i <= N; i++) // for (int j = 1; j <= M; j++) { // if (i > 1 && ves[i][j] == 1 - ves[i - 1][j]) { //递推公式 // Left[i][j] = max(Left[i][j], Left[i - 1][j]); // Right[i][j] = min(Right[i][j], Right[i - 1][j]); // up[i][j] = up[i - 1][j] + 1; // } // int A_instance = Right[i][j] - Left[i][j] + 1; //计算长度 // int B_instance = min(A_instance, up[i][j]); //算出长宽中较小的边,以计算正方形 // temp1 = max(temp1, B_instance * B_instance); //正方形面积 // temp2 = max(temp2, A_instance * up[i][j]); //长方形面积 // } // cout << temp1 << endl << temp2 << endl; // }
A题 Eddy Walker 随机程序找规律和逆元
参考自https://blog.csdn.net/WHY995987477/article/details/97494292
题意:有n个点的环,初始位置在0,可以随机向前走或向后走,问n个位置都走过,并且最后停在m的概率,最后输出前i种情况的概率。
分析:随机数暴力打表找规律,会发现概率是一样的,都等于1/(n - 1)。以及用到求单个逆元, a^(p−2) 就是 a 在 mod p 意义下的逆元
打表的时候n的值不要输入太大!
要分组输入,不要多组输入
另外有两个特殊情况:
1、人一开始站在0上,0已经视为走过了。所以当n == 1 && m == 0 时,开始即终止,概率为1。
2、而当n > 1 && m == 0时,因为走完n个点就结束了,从0点开始走,不可能刚好走完n个点后停在0处,此时的概率是0。
最后要求输出的是每次的结果(概率)的累乘,具体看代码里面的ans。
这里有一个不是用打表找规律做出来的博客:
https://blog.nowcoder.net/n/95f1fe2b38aa4befaba3750128a12786
随机数打表代码:
#includeusing namespace std; const int maxn = 1e5 + 5; int vis[maxn], ans[maxn]; int n;//一共有n个点 int pos;//当前所在位置 int cnt;//已经经过了几个点。 int main() { scanf("%d", &n);//不要用多组样例输入,不要输入太大的n值 srand((unsigned int)(time(NULL))); for(int i = 1; i <= 100000; i++) { memset(vis, 0, sizeof(vis)); vis[0] = 1; pos = 0; cnt = 1; while(cnt < n) { int moving = rand() % 2 ? 1 : -1; pos = (pos + moving + n) % n; if(!vis[pos]) { vis[pos] = 1; cnt++; } if(cnt == n) { ans[pos]++; } } } for(int i = 0;i < n; i++) printf("%d ", ans[i]); printf("\n"); return 0; }
AC代码:
#includeusing namespace std; typedef long long ll; const int mod = 1e9+7; ll qpow(int x,int y){ ll res = 1; while(y){ if(y&1) res= res%mod *x%mod; y>>=1; x = (ll)x%mod*(ll)x%mod; } return res; } int main(){ int T,n,m; scanf("%d",&T); ll res,ans=1; while(T--){ scanf("%d%d",&n,&m); if(n==1 && m==0) res=1; else if(m==0) res=0; else{ res = 1*qpow(n-1,mod-2); } ans = ans%mod * res%mod; printf("%lld\n", ans); } }
D题 Kth Minimum Clique 优先队列优化的bfs
参考自:https://www.cnblogs.com/Chen-Jr/p/11222852.html#autoid-1-0-0
题意:让你求出一张图的第k小团的权值。
分析:
注意,要防止重复,就是每次找最后一位的下一位开始枚举。
#includeusing namespace std; typedef long long ll; const int maxn = 102; bitset a[maxn]; struct node{ ll a; bitset b; bool operator < (const node & t)const{ return a > t.a; } }; priority_queue que; int w[maxn]; char ch[maxn]; int main(){ int n,k; scanf("%d%d",&n,&k); for(int i=0; i ){ scanf("%d", w+i); } for(int i=0; i ){ scanf("%s",ch); for(int j=0; j ){ if(ch[j] == '1'){ a[i].set(j); } } } bitset t; t.reset(); que.push( (node){0,t} ); while(!que.empty()){ node u = que.top(); bitset tmp = u.b; que.pop(); k--; if(k==0){ printf("%lld\n", u.a); return 0; } int pos = 0; for(int i=0; i ){ if(tmp[i]) pos = i+1; ///为了保证不会重复加点,我们每次从一个已经加入的点的后一个结点开始枚举新加入的结点 } for(int i=pos; i ){ if( !tmp[i] && (tmp&a[i])==tmp){ tmp[i] = 1; que.push( (node){u.a + w[i] ,tmp} ); tmp[i] = 0; //记得这个小回溯 } } } puts("-1"); }