P3956 [NOIP2017 普及组] 棋盘
题目描述
有一个m×m的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。
任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的), 你只能向上、 下、左、 右四个方向前进。当你从一个格子走向另一个格子时,如果两个格子的颜色相同,那你不需要花费金币;如果不同,则你需要花费 1个金币。
另外, 你可以花费 2个金币施展魔法让下一个无色格子暂时变为你指定的颜色。但这个魔法不能连续使用, 而且这个魔法的持续时间很短,也就是说,如果你使用了这个魔法,走到了这个暂时有颜色的格子上,你就不能继续使用魔法; 只有当你离开这个位置,走到一个本来就有颜色的格子上的时候,你才能继续使用这个魔法,而当你离开了这个位置(施展魔法使得变为有颜色的格子)时,这个格子恢复为无色。
现在你要从棋盘的最左上角,走到棋盘的最右下角,求花费的最少金币是多少?
//棋盘第一版: 普通搜索 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<climits> using namespace std; int mx[] = {0,0,1,0,-1}; int my[] = {0,1,0,-1,0}; int m,n,ans = INT_MAX, vis[105][105], c[105][105], g[105][105]; void dfs(int x, int y, int sum, int col, int f){ // cout<<"hhh: "<<x<<" "<<y<<" "<<col<<" "<<sum<<endl; if(sum >= ans) return; if(sum >= g[x][y]) return;//第二版 if(x == m && y == m){ ans = min(ans, sum); return; } g[x][y] = sum;// for(int i = 1; i <= 4; i++){ int xx = x + mx[i]; int yy = y + my[i]; if(xx < 1 || yy < 1 || xx > m || yy > m) continue;//这里写成了n if(vis[xx][yy]) continue; if(c[xx][yy] == -1 && f == 1) continue;// vis[xx][yy] = 1; if(c[xx][yy] != -1){ if(c[xx][yy] == col) dfs(xx, yy, sum, col, 0); else if(c[xx][yy] != col) dfs(xx, yy, sum+1, c[xx][yy], 0); } else dfs(xx, yy, sum+2, col, 1); vis[xx][yy] = 0; } } int main(){ scanf("%d%d",&m,&n); int x, y, col; memset(c, 0xff, sizeof c); memset(g, 1, sizeof g); for(int i = 1; i <= n; i++){ scanf("%d%d%d",&x,&y,&col); c[x][y] = col; } vis[1][1] = 1; dfs(1, 1, 0, c[1][1], 0); if(ans != INT_MAX) printf("%d\n",ans); else printf("-1"); return 0; }
P1784 数独
#include<bits/stdc++.h>
using namespace std;
int x,t=81,flag;
int a[10][10],row[10][10],col[10][10],block[10][10],nxt[15];
int ju[10][10]={
0,0,0,0,0,0,0,0,0,0,
0,1,1,1,2,2,2,3,3,3,
0,1,1,1,2,2,2,3,3,3,
0,1,1,1,2,2,2,3,3,3,
0,4,4,4,5,5,5,6,6,6,
0,4,4,4,5,5,5,6,6,6,
0,4,4,4,5,5,5,6,6,6,
0,7,7,7,8,8,8,9,9,9,
0,7,7,7,8,8,8,9,9,9,
0,7,7,7,8,8,8,9,9,9,
};
struct Node{
int cnt,id;
}hang[15];
bool cmp(Node x, Node y){
return x.cnt > y.cnt;
}
void dfs(int rm, int tx, int ty) {
if(rm==0) {
flag=1;
// ans=max(ans,now);
for(int i = 1; i <= 9; i++){
for(int j = 1; j <= 9; j++)
printf("%d ",a[i][j]);
printf("\n");
}
// return;
exit(0);
}
if(ty == 10) tx = nxt[tx], ty=1;
// if(ty == 10) dfs(rm, nxt[tx], ty);
if(a[tx][ty]) dfs(rm,tx,ty+1);
else{
int tc=ju[tx][ty];
for(int i=1;i<=9;i++) //先放置可选择数目最少的空位,如果在上面可以记录可以放那些数更好
if(!row[tx][i] && !col[ty][i] && !block[tc][i]){
// cout<<tx<<" "<<ty<<" "<<i<<endl;
a[tx][ty]=i;
row[tx][i]=1;
col[ty][i]=1;
block[tc][i]=1;
dfs(rm-1, tx, ty+1);
row[tx][i]=0;
col[ty][i]=0;
block[tc][i]=0;
a[tx][ty]=0;
}
}
}
int main()
{
for(int i=1;i<=9;i++){
int num = 0;
hang[i].id = i;
for(int j=1;j<=9;j++) {
scanf("%d",&a[i][j]);
if(a[i][j]>0) {
row[i][a[i][j]]=1;
col[j][a[i][j]]=1;
block[ju[i][j]][a[i][j]]=1;
t--;
num++;
}
}
hang[i].cnt = num;
}
sort(hang + 1, hang + 10, cmp);
for(int i = 1; i <= 8; i++){
nxt[hang[i].id] = hang[i+1].id;
// cout<<hang[i].id<<endl;
// cout<<hang[i].id<<" "<<hang[i+1].id<<endl;
}
dfs(t, hang[1].id, 1);
// if(flag) printf("%d\n",ans);
// else printf("-1\n");
// cout<<clock()<<endl;
return 0;
}
原来的问题是,传进去sum,如果所有的空缺都填上了,就退出了,因此原有的数字不会被算进去。
void dfs(int rm, int tx, int ty, int sum) {
if(sum + rm * 90 < mx) return;
if(rm==0) {
fl = 1;
if(sum > mx){
mx = sum;
}
return;
}
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int x,t=81,flag;
int a[15][15],row[15][15],col[15][15],block[15][15],nxt[15],mx,fl,b[15][15];
int ju[10][10]={
0,0,0,0,0,0,0,0,0,0,
0,1,1,1,2,2,2,3,3,3,
0,1,1,1,2,2,2,3,3,3,
0,1,1,1,2,2,2,3,3,3,
0,4,4,4,5,5,5,6,6,6,
0,4,4,4,5,5,5,6,6,6,
0,4,4,4,5,5,5,6,6,6,
0,7,7,7,8,8,8,9,9,9,
0,7,7,7,8,8,8,9,9,9,
0,7,7,7,8,8,8,9,9,9,
};
int v[10][10]={
0,0,0,0,0, 0,0,0,0,0,
0,6,6,6,6, 6,6,6,6,6,
0,6,7,7,7, 7,7,7,7,6,
0,6,7,8,8, 8,8,8,7,6,
0,6,7,8,9, 9,9,8,7,6,
0,6,7,8,9,10,9,8,7,6,
0,6,7,8,9, 9,9,8,7,6,
0,6,7,8,8, 8,8,8,7,6,
0,6,7,7,7, 7,7,7,7,6,
0,6,6,6,6, 6,6,6,6,6
};
struct Node{
int cnt,id;
}hang[15];
bool cmp(Node x, Node y){
return x.cnt > y.cnt;
}
void dfs(int rm, int tx, int ty) {
if(rm==0) {
fl = 1;
int res = 0;
for(int i = 1; i <= 9; i++)
for(int j = 1; j <= 9; j++)
res += a[i][j] * v[i][j];
mx = max(mx, res);
return;
}
if(ty == 10) tx = nxt[tx], ty=1;
// if(ty == 10) dfs(rm, nxt[tx], ty);
if(a[tx][ty]) dfs(rm, tx, ty+1);
else{
int tc = ju[tx][ty];
for(int i=1;i<=9;i++) //先放置可选择数目最少的空位,如果在上面可以记录可以放那些数更好
if(!row[tx][i] && !col[ty][i] && !block[tc][i]){
// cout<<tx<<" "<<ty<<" "<<i<<endl;
a[tx][ty]=i;
row[tx][i]=1;
col[ty][i]=1;
block[tc][i]=1;
dfs(rm-1, tx, ty+1);
row[tx][i]=0;
col[ty][i]=0;
block[tc][i]=0;
a[tx][ty]=0;
}
}
}
int main()
{
for(int i=1;i<=9;i++){
int num = 0;
hang[i].id = i;
for(int j=1;j<=9;j++) {
scanf("%d",&a[i][j]);
if(a[i][j]>0) {
row[i][a[i][j]]=1;
col[j][a[i][j]]=1;
block[ju[i][j]][a[i][j]]=1;
t--;
num++;
}
}
hang[i].cnt = num;
}
sort(hang + 1, hang + 10, cmp);
for(int i = 1; i <= 8; i++){
nxt[hang[i].id] = hang[i+1].id;
}
dfs(t, hang[1].id, 1);
if(fl)cout<<mx<<endl;
else cout<<-1<<endl;
return 0;
}
P4573 [CQOI2013]新数独
这道题的难点在于读入的处理,b[i][j][i+1][j] =1/2的表示关系大小。
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
int row[10][10],col[10][10],g[10][10],b[10][10][10][10];
int block[10][10]=
{
{ 0,0,0,0, 0,0,0, 0,0,0 },
{ 0,1,1,1, 2,2,2, 3,3,3 },
{ 0,1,1,1, 2,2,2, 3,3,3 },
{ 0,1,1,1, 2,2,2, 3,3,3 },
{ 0,4,4,4, 5,5,5, 6,6,6 },
{ 0,4,4,4, 5,5,5, 6,6,6 },
{ 0,4,4,4, 5,5,5, 6,6,6 },
{ 0,7,7,7, 8,8,8, 9,9,9 },
{ 0,7,7,7, 8,8,8, 9,9,9 },
{ 0,7,7,7, 8,8,8, 9,9,9 },
};
int a[10][10];
int flag;
void print(){
for(int i=1;i<=8;i++){
for(int j=1;j<=9;j++)
printf("%d ",a[i][j]);
printf("\n");
}
for(int j=1;j<=9;j++)
printf("%d ",a[9][j]);
}
int tot = 0;
void work(int x,int y){
if(x==9 && y==10) {flag=1;print();exit(0);}
else if(y==10) x++,y=1;
tot++;
if(a[x][y]) work(x,y+1);
else{
for(int i=1;i<=9;i++){
if(row[x][i]==0 && col[y][i]==0 && g[block[x][y]][i]==0){
if(x-1 >= 1){
if(b[x-1][y][x][y] == 1){
if(i >= a[x-1][y]) continue;
}
else if(b[x-1][y][x][y] == 2)
if(i <= a[x-1][y]) continue;
}
if(y-1 >= 1){
if(b[x][y-1][x][y] == 1){
if(i >= a[x][y-1]) continue;
}
else if(b[x][y-1][x][y] == 2)
if(i <= a[x][y-1]) continue;
}
row[x][i]=1;
col[y][i]=1;
g[block[x][y]][i]=1;
a[x][y]=i;
work(x,y+1);
row[x][i]=0;
col[y][i]=0;
g[block[x][y]][i]=0;
a[x][y]=0;
}
if(flag==1) return;
}
}
}
int main(){
for(int i = 1; i <= 9; i++) {
int di = i+1; char ch;
for(int j = 1; j <= 8; j++){
if(j%3 == 0) continue;
cin>>ch; b[i][j][i][j+1] = (ch == '>'? 1:2);
}
if(i % 3 == 0) continue;
for(int j = 1; j<=9; j++){
cin>>ch;
b[i][j][di][j] = (ch=='v'?1:2);
}
}
work(1,1);
}
P3052 [USACO12MAR]Cows in a Skyscraper G
递归箱子,枚举物品,18!
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int n,a[23],W,ans=INT_MAX,b[23],k;
void dfs(int t, int w, int cnt){//递归缆车,枚举猫
if(cnt >= ans) return;
if(t == n+1){
cout<<cnt<<endl;
ans = min(ans, cnt);
return;
}
for(int i = 1; i <= n; i++){
if(b[i]) continue;
b[i] = 1;
if(w + a[i] <= W){
dfs(t+1, w+a[i], cnt);
}
else{
dfs(t+1, a[i], cnt+1);
}
b[i] = 0;
}
}
int main(){
scanf("%d%d",&n,&W);
for(int i = 1; i <= n; i++){
scanf("%d",&a[i]);
}
sort(a+1,a+n+1,greater<int>());
dfs(1,0,1);
printf("%d\n",ans);
return 0;
}
/*
6 10
4 9 7 3 6 5
4
*/
递归物品,枚举放到哪个箱子,迭代加深思想
#include <algorithm> #include <iostream> using namespace std; const int N = 2e1; int cat[N], cab[N]; int n, w; int ans; bool cmp(int a, int b) { return a > b; } void dfs(int now, int cnt) { if (cnt >= ans) { return; } if (now == n + 1) { ans = min(ans, cnt); return; // exit(0); } //尝试分配到已经租用的缆车上 for (int i = 1; i <= cnt; i++) { //分配到已租用缆车 if (cab[i] + cat[now] <= w) { cab[i] += cat[now]; dfs(now + 1, cnt); cab[i] -= cat[now]; //还原 } } cab[cnt + 1] = cat[now]; dfs(now + 1, cnt + 1); cab[cnt + 1] = 0; // 新开一辆缆车 } int main() { cin >> n >> w; for (int i = 1; i <= n; i++) { cin >> cat[i]; } sort(cat + 1, cat + 1 + n, cmp); ans = n; dfs(1, 0); cout << ans << endl; return 0; }
状态压缩
小木棍
第一版:
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; int sum, mx, len, n, a[70], b[70]; void dfs(int cnt, int s, int las){ if(cnt == sum/len+1){ printf("%d\n",len); exit(0); } for(int i = las; i <= n; i++){ if(s + a[i] > len) continue; if(b[i]) continue; b[i] = 1; if(s + a[i] == len) dfs(cnt+1, 0, 1); else dfs(cnt, s+a[i], i+1); b[i] = 0; } } int main(){ scanf("%d",&n); int x; for(int i = 1; i <= n; i++){ scanf("%d",&x); if(x > 50) continue; a[i] = x; sum += x; mx = max(mx, x); } sort(a+1, a+n+1, greater<int>()); for(len = mx; len <= sum/2; len++){ if(sum % len == 0){ dfs(1, 0, 1); } } printf("%d\n",sum); return 0; }
第二版:
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; int sum, mx, len, n, a[70], b[70]; void dfs(int cnt, int s, int las){ if(cnt == sum/len+1){ printf("%d\n",len); exit(0); } for(int i = las; i <= n; i++){ if(s + a[i] > len) continue; if(b[i]) continue; b[i] = 1; if(s + a[i] == len) dfs(cnt+1, 0, 1); else dfs(cnt, s+a[i], i+1); b[i] = 0; while(a[i] == a[i+1] && i+1<=n) i++; if(s + a[i] == len) return;//s+5(当前)==len,之后的失败了,那么s+(4+1)==len,然后用5去拼其他的也是失败 if(s == 0) return;//这个小木棍作为目标值的第一根,如果失败了。那么就不需要再用下面的i++作为第一根了,因为这一根总要作为第一根出现
} } int main(){ scanf("%d",&n); int x; for(int i = 1; i <= n; i++){ scanf("%d",&x); if(x > 50) continue; a[i] = x; sum += x; mx = max(mx, x); } sort(a+1, a+n+1, greater<int>()); for(len = mx; len <= sum/2; len++){ if(sum % len == 0){ dfs(1, 0, 1); } } printf("%d\n",sum); return 0; }
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; int n,a[70],cnt,sum,x,mx,b[3333],vis[70]; void dfs(int k,int s,int S,int las){ if(k==sum/S+1){ cout<<S<<endl; exit(0);//结束整个程序 } for(int i=las+1;i<=n;i++){ //从 las+1 开始,优化 3 if(s+a[i]>S) continue; if(vis[i]) continue; vis[i]=1; if(s+a[i]==S) dfs(k+1,0,S,0); else dfs(k,s+a[i],S,i); vis[i]=0; if(s+a[i]==S) return;//优化 7 if(s==0) return;// 优化 6 while(a[i]==a[i+1] && i<n) i++;//优化 5 19 } } } int main(){ cin>>n; for(int i=1;i<=n;i++){ cin >> x; if(x>50) continue; //题目中的提示 a[++cnt]=x; sum+=x; mx=max(mx,x); } n=cnt; cnt=0; sort(a+1,a+n+1,greater<int>());//优化 4 if(mx==1){ cout<<1<<endl; return 0; } for(int i=2;i*i<=sum;i++) // 以下是优化 1 if(sum%i==0){ if(i>=mx) b[++cnt]=i; if(i==sum/i) break; b[++cnt]=sum/i; } sort(b+1,b+cnt+1);// 以下是优化 2 for(int i=1;i<=cnt;i++) dfs(1,0,b [i],0); cout<<sum<<endl; return 0; }
CF193A Cutting Figure
题目背景
你有一块n×m的方格纸。其中有一些方格已被涂上颜色。 我们把已被涂上颜色的方格全部放入一个集合A内,A中所有的方格都是相互联通的(联通即任意两个方格之间都存在一条路径 路径上的每一个方格都是已涂上颜色的,并且每一个方格都与前一个方格有一条公共边)。 我们想知道 最少删除多少个方格 才能使这张方格纸上有颜色的方格不再联通。 根据定义,只有一个方格的集合或者不包含任何方格的集合也是联通的。
输入
输入第一行包含两个数 n和m 分别代表方格纸的高和宽 接下来的n行 每行有m个字符: “#”表示已被涂色的方格; “.”表示未被涂色的方格。 输入的方格纸保证所有有颜色的方格都是相互连通的。
输出
输出最少需要删除几个方格才能使图变得不联通, 假如无法做到 输出-1。
思路
通过枚举我们可以发现,无论什么图,答案都是三种情况: -1,1,2。
所以我们分类讨论:
第一种:答案为 -1。显然,这种情况就是 #
小于 3 个的时候。
第二种:答案为 1。我们枚举每一个 #
,假设将它删去,再进行深搜,判断是否为联通图。
第三种:答案为 2。就是不满足第一种也不满足第二种的情况。
输入
输入第一行包含两个数 n和m 分别代表方格纸的高和宽 接下来的n行 每行有m个字符: “#”表示已被涂色的方格; “.”表示未被涂色的方格。 输入的方格纸保证所有有颜色的方格都是相互连通的。
输出
输出最少需要删除几个方格才能使图变得不联通, 假如无法做到 输出-1。
分析:一个性质:最多两个点,比如:
1 1 1
1 1 1
1 1 1
(1,2),(2,1)即可
#include<bits/stdc++.h> using namespace std; int n,m, vis[55][55], s, t, tot, ca, cnt; int mx[] = {0, 0, 1, -1, 0}; int my[] = {0, 1, 0, 0, -1}; char a[55][55]; //void dfs(int x, int y){ // for(int i = 1; i <= 4; i++){ // int xx = x + mx[i], yy = y + my[i]; // if(xx >= 1 && xx <= n && yy >= 1 && yy <= m){ // vis[xx][yy] = 1; // if(!vis[xx][yy] && a[xx][yy] == '#'){ // cnt--; // dfs(xx, yy); // } // } // } //} void dfs(int x,int y) { if (x<1||x>n||y<1||y>m) return; vis[x][y]=true; //注意,这里的优化,走到了就标记(省的重复走),并不是"#" 才标记 for(int w=1;w<=4;++w){ int xx=x+mx[w],yy=y+my[w]; if (a[xx][yy]=='#'&&!vis[xx][yy]){ cnt--; dfs(xx,yy); } } return; } void work(int s, int t){ for(int k = 1; k <= 4; k++){ int sx = s + mx[k], sy = t + my[k]; if(sx >= 1 && sx <= n && sy >= 1 && sy <= m && a[sx][sy] == '#'){ vis[sx][sy] = 1; --cnt; dfs(sx, sy); break; } } } int main(){ scanf("%d%d",&n,&m); for(int i = 1; i<=n; i++) scanf("%s",a[i]+1); for(int i = 1; i <= n; i++){ for(int j = 1; j <= m; j++){ if(a[i][j] == '#') tot++; } } cnt = tot; if(tot <= 2){printf("-1\n"); return 0;} for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++){ if(a[i][j] == '#'){ memset(vis, 0, sizeof vis); cnt = tot-1; vis[i][j] = 1; work(i,j); if(cnt){ printf("1\n"); return 0; } } } printf("2\n"); return 0; } /* 3 4 #### #.#. ###. 1 */ /* 2 3 ### ### 2 */
算24点
```
/*
3* 8
4 1 2 6
1 1 1 8
1+2+3+18
1 2 3 18
*/
/*
不保证中间可以除尽
7 7 7 9
7 * 7 = 49
9 - 7 = 2
49 / 2 = 24
*/
/*
9 7 5 7
(9−7)×(5+7)=24
*/
//每次选择两个数进行求和
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<climits> using namespace std; int a[5], ra[5], rb[5], rd[5]; char rc[5]; void dfs(int t){ if(t == 4){ // cout<<"ha? "<<endl; int fl = 0, sum = 0; for(int i = 1; i <= 4; i++){ sum += a[i]; if(a[i] == 24) fl = 1; } if(sum == 24 && fl == 1){ for(int i = 1; i <= 3; i++){ printf("%d%c%d=%d\n",ra[i],rc[i],rb[i],rd[i]); } exit(0); } return; } for(int i = 1; i <= 4; i++){ if(!a[i]) continue; // cout<<"a[i]: "<<a[i]<<" "<<i<<endl; for(int j = 1; j <= 4; j++){ if(i == j) continue; if(!a[j]) continue; if(a[i] < a[j]) continue;//key:2*(2/4)是不合法的 // cout<<"a[j]: "<<a[j]<<" "<<j<<endl; ra[t] = a[i], rb[t] = a[j]; rc[t] = '+'; a[i] = rd[t] = ra[t] + rb[t]; a[j] = 0; // cout<<"p1-ppp: "<<rd[t]<<endl; dfs(t+1); a[i] = ra[t], a[j] = rb[t]; rc[t] = '-'; a[i] = rd[t] = ra[t] - rb[t]; a[j] = 0; // cout<<"p2-ppp: "<<rd[t]<<endl; dfs(t+1); a[i] = ra[t], a[j] = rb[t]; rc[t] = '*'; a[i] = rd[t] = ra[t] * rb[t]; a[j] = 0; // cout<<"p3-ppp: "<<rd[t]<<endl; dfs(t+1); a[i] = ra[t], a[j] = rb[t]; if(ra[t] % rb[t] != 0) continue; rc[t] = '/'; a[i] = rd[t] = ra[t] / rb[t]; a[j] = 0; // cout<<"p4-ppp: "<<rd[t]<<endl; dfs(t+1); a[i] = ra[t], a[j] = rb[t]; } } } int main(){ for(int i = 1; i <= 4; i++){ scanf("%d",&a[i]); } dfs(1); printf("No answer!\n"); return 0; }