小猫爬山
翰翰和达达饲养了 N 只小猫,这天,小猫们要去爬山。
经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。
翰翰和达达只好花钱让它们坐索道下山。
索道上的缆车最大承重量为 W,而 N 只小猫的重量分别是 C1、C2......CN。
当然,每辆缆车上的小猫的重量之和不能超过 W。
每租用一辆缆车,翰翰和达达就要付 1 美元,所以他们想知道,最少需要付多少美元才能把这 N 只小猫都运送下山?
输入格式
第 1 行:包含两个用空格隔开的整数,N 和 W。
第 2..N+1 行:每行一个整数,其中第 i+1 行的整数表示第 i 只小猫的重量 Ci。
输出格式
输出一个整数,表示最少需要多少美元,也就是最少需要多少辆缆车。
数据范围
1≤N≤18,
1≤Ci≤W≤108
输入样例:
5 1996
1
2
1994
12
29
输出样例:
2
代码1(二分 + 可行性判断 DFS )
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.StringTokenizer;
public class Main {
static int N=10010,id=1,id1=1,n,m,w;
static Integer a[]=new Integer[20];
static int group[]=new int[20];
static long capacity[]=new long[20];//每艘船的剩余容量
//static boolean f[][]=new boolean[20][20];
static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
StringTokenizer st=new StringTokenizer(br.readLine());
n=Integer.parseInt(st.nextToken());
w=Integer.parseInt(st.nextToken());
Arrays.fill(capacity, w);
for (int i = 0; i < n; i++) {
a[i]=Integer.parseInt(br.readLine().trim());
}
Arrays.sort(a, 0, n, Collections.reverseOrder());//从大到小枚举 可以大大减少分支数量
int l=1,r=18;
while(l<r){
int mid=(l+r)>>1;
if(dfs(mid,0)){
r=mid;
}else{
l=mid+1;
}
}
bw.write(l+"");
bw.flush();
bw.close();
bw.close();
}
static boolean dfs(int tot,int k){
if(k==n){
return true;
}
boolean res=false;
for (int i = 1; i <= tot; i++) {//枚举那艘船可以放下a[k]
if(res)break;
if(capacity[i]>=a[k]){
capacity[i]-=a[k];
if(dfs(tot, k+1)){
res=true;
}
capacity[i]+=a[k];
}
if (capacity[i] == w) break; // w 是初始容量 注意要加这一行剪枝 如果说当前的这个物品连最大的容量也放不下,那就没有必要再去枚举其他的船了。
}
return res;
}
}
代码2(单次 DFS + 最优性剪枝)
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;
public class Main {
static int N=10010,id=1,id1=1,n,m,w,t,res=Integer.MAX_VALUE;
static Integer a[]=new Integer[20];
static long capacity[]=new long[20];//每艘船的剩余容量
//static boolean f[][]=new boolean[20][20];
static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
StringTokenizer st=new StringTokenizer(br.readLine());
n=Integer.parseInt(st.nextToken());
w=Integer.parseInt(st.nextToken());
for (int i = 0; i < n; i++) {
a[i]=Integer.parseInt(br.readLine().trim());
}
Arrays.sort(a, 0, n, Collections.reverseOrder());//倒序遍历
dfs(1,0);
bw.write(res+"");
bw.flush();
bw.close();
bw.close();
}
static void dfs(int k,int u){//当前已经用了k辆车 现在正在考虑a[k]物品
if(k>res)return;
if(u==n){//能到这 一定是最终的答案 因为有好多剪枝
res=k;
return;
}
//可以选择放到现有的k辆车的某一个中
for (int i = 1; i <= k; i++) {
if(capacity[i]+a[u]<=w){//可以装得下
capacity[i]+=a[u];
dfs(k, u+1);
capacity[i]-=a[u];//恢复现场
}
}
//也可以直接开一个新车
capacity[++k]+=a[u];
dfs(k, u+1);
capacity[k--]-=a[u];
}
}
数独
数独 是一种传统益智游戏,你需要把一个 9×9 的数独补充完整,使得数独中每行、每列、每个 3×3 的九宫格内数字 1∼9 均恰好出现一次。
请编写一个程序填写数独。
输入格式
输入包含多组测试用例。
每个测试用例占一行,包含 81 个字符,代表数独的 81 个格内数据(顺序总体由上到下,同行由左到右)。
每个字符都是一个数字(1−9)或一个 .(表示尚未填充)。
您可以假设输入中的每个谜题都只有一个解决方案。
文件结尾处为包含单词 end 的单行,表示输入结束。
输出格式
每个测试用例,输出一行数据,代表填充完全后的数独。
输入样例:
4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
end
输出样例:
417369825632158947958724316825437169791586432346912758289643571573291684164875293
416837529982465371735129468571298643293746185864351297647913852359682714128574936
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Arrays;
public class Main {
static int N=10010,id=1,id1=1,n,m,w,t,res=Integer.MAX_VALUE;
static char c[]=new char[82];
//static boolean f[]=new boolean[82];
static boolean ge[][]=new boolean[9][9];//ge[i][j]表示将9×9的矩阵图看成9个3×3的矩阵之后第i个矩阵存不存在数字j
static boolean row[][]=new boolean[9][9];//row[i][j]表示第i行有没有数字j
static boolean col[][]=new boolean[9][9];//col[i][j]表示第i列有没有数字j
static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
// StringTokenizer st=new StringTokenizer(br.readLine());
// n=Integer.parseInt(st.nextToken());
// w=Integer.parseInt(st.nextToken());
String line;
while(!(line=br.readLine()).equals("end")){
c=line.toCharArray();
for (int i = 0; i < 9; i++) {
Arrays.fill(ge[i], false);
Arrays.fill(row[i], false);
Arrays.fill(col[i], false);
}
for (int i = 0; i < 81; i++) {
if(c[i]!='.'){
c[i]=(char)(c[i]-1);
int num=c[i]-'0';
int x=i/9,y=i%9;
int gx=x/3,gy=y/3;
ge[gx*3+gy][num]=true;
row[x][num]=true;
col[y][num]=true;
}
}
dfs();
}
bw.flush();
bw.close();
bw.close();
}
static boolean dfs() throws IOException{
int k=-1,mincnt=Integer.MAX_VALUE;//找到一个最受限的位置
for (int j = 0; j < 81; j++) {
if(c[j]!='.')continue;
int x=j/9,y=j%9;
int cnt=0;
for (int i = 0; i < 9; i++) {
if(!row[x][i] && !col[y][i] && !ge[x/3*3+y/3][i]){
cnt++;
}
}
if(cnt<mincnt){
k=j;
mincnt=cnt;
if (cnt == 1) break; // 唯一候选,就是最佳位置,无需再找
}
}
if(k==-1){
StringBuilder stringBuilder=new StringBuilder();
for (int i = 0; i < 81; i++) {
stringBuilder.append((char)(c[i]+1));
}
bw.write(stringBuilder.toString()+"\n");
return true;
}
int x=k/9,y=k%9;
for (int i = 0; i < 9; i++) {
if(!row[x][i] && !col[y][i] && !ge[x/3*3+y/3][i]){
row[x][i]=true;col[y][i]=true;ge[x/3*3+y/3][i]=true;
c[k]=(char)(i+'0');
if(dfs()){
return true;
}
row[x][i]=false;col[y][i]=false;ge[x/3*3+y/3][i]=false;
c[k]='.';
}
}
return false;
}
}
木棒
乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过 50 个长度单位。
然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。
请你设计一个程序,帮助乔治计算木棒的可能最小长度。
每一节木棍的长度都用大于零的整数表示。
输入格式
输入包含多组数据,每组数据包括两行。
第一行是一个不超过 64 的整数,表示砍断之后共有多少节木棍。
第二行是截断以后,所得到的各节木棍的长度。
在最后一组数据之后,是一个零。
输出格式
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。
数据范围
数据保证每一节木棍的长度均不大于 50。
输入样例:
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
输出样例:
6
5
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;
public class Main {
static int N=110,id=1,id1=1,n,m;
static Integer a[]=new Integer[N];
static int group[]=new int[N];
static boolean f[]=new boolean[N];
static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
// StringTokenizer st=new StringTokenizer(br.readLine());
// n=Integer.parseInt(st.nextToken());
// w=Integer.parseInt(st.nextToken());
String line;
while(!(line=br.readLine()).equals("0")){
n=Integer.parseInt(line);
StringTokenizer st=new StringTokenizer(br.readLine());
int l=Integer.MIN_VALUE;
int sum=0;
for (int i = 0; i < n; i++) {
a[i]=Integer.parseInt(st.nextToken());
l=Math.max(a[i], l);
sum+=a[i];
}
Arrays.sort(a,0,n,Collections.reverseOrder());
for (int i = l; i <= sum; i++) {//依次枚举最小可能长度
if(sum%i==0){
Arrays.fill(f, false);
if(dfs(0,i,0,i,sum/i)){
bw.write(i+"\n");
break;
}
}
}
}
bw.flush();
br.close();
bw.close();
}
static boolean dfs(int k,int capbility,int start,int len,int groupnum) throws IOException{
//当前在拼k组 当前正在拼的k组还需要多少才能拼出
//Start保证 在拼一整个木块的时候,按照递增的顺序去选中 这样就可以防止很多重复的一些组合。
//原来整个木块儿的长度以及需要拼成的组数
if(k==groupnum){
return true;
}
for (int i = start; i < n; i++) {
if(!f[i]){//当前木棍没有被用过
f[i]=true;
if(capbility>a[i]){
if(dfs(k, capbility-a[i],i+1, len, groupnum)){
return true;
}
}else if(capbility==a[i]){
if(dfs(k+1, len,0, len, groupnum)){
return true;
}
}
f[i]=false;
//剪枝1
//a[i] 是当前全局最长的未用木棍,它迟早要做某个空组的开头
if (capbility == len) return false;//start==0和capbility == len一定是同时发生的
//剪枝2
//假设存在某个成功的拼法,其中当前组不是用 a[i] 填满的,而是用几根更短的木棍
//(比如 x + y)凑出
//capbility,然后把 a[i] 留到后面的某一组使用。那么结果一定也是false
if (capbility == a[i]) return false;
while(i+1<n && a[i+1]==a[i])i++;//跳过与我相同的情况 因为一定不会成功
}
}
return false;
}
}